From 8341d2660b1b0922fe607045cd0df1ab27200e3c Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Sat, 6 Sep 2014 07:43:44 -0700 Subject: [PATCH] updates to docs (including third tutorial) --- doc/Acknowledgments.html | 2 +- doc/Backstory.html | 2 +- doc/Cache.html | 2 +- doc/Decode.html | 2 +- doc/File_formats.html | 2 +- doc/Installation.html | 2 +- doc/License___source_code.html | 2 +- doc/Module_reference.html | 2 +- doc/Pagetree.html | 2 +- doc/Render.html | 4 +- doc/Tag.html | 2 +- doc/Template.html | 2 +- doc/Top.html | 2 +- doc/World.html | 4 +- doc/big-picture.html | 2 +- doc/doc-index.html | 2 +- doc/file-types.html | 2 +- doc/first-tutorial.html | 13 +- doc/index.html | 4 +- doc/mb.scss | 45 ++ doc/quick-tour.html | 4 +- doc/raco-pollen.html | 2 +- doc/reader.html | 16 +- doc/second-tutorial.html | 9 +- doc/third-tutorial.html | 7 + scribblings/command.scrbl | 4 +- scribblings/format-test.scrbl | 47 ++ scribblings/formats.scrbl | 4 +- scribblings/mb-tools.rkt | 46 ++ scribblings/mb.scss | 45 ++ scribblings/pollen.scrbl | 2 +- scribblings/quick.scrbl | 113 +++-- scribblings/tag.scrbl | 24 +- scribblings/top.scrbl | 2 +- scribblings/tutorial-first.scrbl | 186 ++++---- scribblings/tutorial-fourth.scrbl | 100 +++++ scribblings/tutorial-second.scrbl | 290 ++++++++---- scribblings/tutorial-third.scrbl | 718 ++++++++++++++++++++++++++++-- scribblings/world.scrbl | 8 +- 39 files changed, 1409 insertions(+), 318 deletions(-) create mode 100644 doc/mb.scss create mode 100644 doc/third-tutorial.html create mode 100644 scribblings/format-test.scrbl create mode 100644 scribblings/mb-tools.rkt create mode 100644 scribblings/mb.scss create mode 100644 scribblings/tutorial-fourth.scrbl diff --git a/doc/Acknowledgments.html b/doc/Acknowledgments.html index 1e3e332..4e7aea4 100644 --- a/doc/Acknowledgments.html +++ b/doc/Acknowledgments.html @@ -1,2 +1,2 @@ -11 Acknowledgments
6.1.0.5

11 Acknowledgments

One curious aspect of free software is that you can appropriate the benefits of other people’s work while making it look like your own. No such legerdemain here. Whatever effort I’ve put into Pollen is dwarfed by the epic accomplishments of the Racket development team. I thank all of them — especially Matthew Flatt, Jay McCarthy, and Matthias Felleisen — for making this tremendous tool available, for adding several features I suggested, and for patiently answering my dumb questions over the months.

Thank you to Greg Hendershott for his Markdown parser.

The best software tools do more than get the job done. They create an incentive to undertake jobs you wouldn’t have attempted before. Racket encouraged me to become a better programmer so I could create Pollen. Likewise, I hope that Pollen encourages you to make things you couldn’t before.

MB

 
\ No newline at end of file +12 Acknowledgments
6.1.0.5

12 Acknowledgments

One curious aspect of free software is that you can appropriate the benefits of other people’s work while making it look like your own. No such legerdemain here. Whatever effort I’ve put into Pollen is dwarfed by the epic accomplishments of the Racket development team. I thank all of them — especially Matthew Flatt, Jay McCarthy, and Matthias Felleisen — for making this tremendous tool available, for adding several features I suggested, and for patiently answering my dumb questions over the months.

Thank you to Greg Hendershott for his Markdown parser.

The best software tools do more than get the job done. They create an incentive to undertake jobs you wouldn’t have attempted before. Racket encouraged me to become a better programmer so I could create Pollen. Likewise, I hope that Pollen encourages you to make things you couldn’t before.

MB

 
\ No newline at end of file diff --git a/doc/Backstory.html b/doc/Backstory.html index e43bed1..5080b89 100644 --- a/doc/Backstory.html +++ b/doc/Backstory.html @@ -1,2 +1,2 @@ -3 Backstory
6.1.0.5

3 Backstory

I created Pollen to overcome limitations & frustrations I repeatedly encountered with existing web-publishing tools.

If you agree with my characterization of those problems, then you’ll probably like the solution that Pollen offers. If not, you probably won’t.

3.1 Web development and its discontents

I made my first web page in 1994, shortly after the web was invented. I opened my text editor (at the time, BBEdit), pecked out <html><body>Hello world</body></html>, then loaded it in Mosaic. So did a million other nerds.

If you weren’t around then, you didn’t miss much. Everything about the web was horrible: the web browsers, the computers running the browsers, the dial-up connections feeding the browsers, and of course HTML itself. At that point, the desktop-software experience was already slick and refined. By comparison, using the web felt like banging rocks together.

That’s no longer true. The web is now 20 years old. During that time, most parts of the web have improved dramatically — for instance, the connections are faster, the browsers are more sophisticated, the screens have more pixels.

But one part has not improved: the way we make web pages. Over the years, tools promising to simplify HTML development have come and mostly gone — from PageMill to Dreamweaver to WordPress to Jekyll. Meanwhile, true web jocks have remained loyal to the original HTML power tool: the humble text editor.

In one way, this makes sense. Web pages are mostly made of text-based data — HTML, CSS, JavaScript, and so on — and the simplest way to mainpulate this data is with a text editor. While HTML and CSS are not programming languages, they lend themselves to semantic and logical structure that’s most easily expressed by editing them as text. Furthermore, text-based editing makes debugging and performance improvements easier.

But text-based editing is also limited. Though the underlying description of a web page is notionally human-readable, it’s largely optimized to be readable by other software — namely, web browsers. HTML markup in particular is verbose and easily mistyped. And isn’t it fatally dull to manage all the boilerplate, like surrounding every paragraph with <p>...</p>? Yes, it is.

For these reasons, much of web development should lend itself to abstraction & automation. Abstraction means consolidating repetitve, complex patterns into simpler, parameterized forms. Automation means avoiding the manual drudgery of generating the output files. But in practice, tools that enable this abstraction & automation have been slow to arrive, and most have come hobbled with unacceptable deficiencies.

3.2 The better idea: a programming model

Parallel with my HTML education, I also goofed around with various programming languages — C, C++, Perl, Java, PHP, JavaScript, Python. Unlike HTML, programming languages excel at abstraction and automation. This seemed like the obvious direction for web development to go.

What distinguishes the text-editing model from the programming model? It’s a matter of direct vs. indirect manipulation of output. The text-editing model treats HTML as something to be written directly with a text editor. Whereas the programming model treats HTML — or whatever the output is — as the result of compiling a set of source files, which are written in a programming language. The costs of working indirectly via the programming language are offset by the benefits of abstraction & automation.

On the early web, the text-editing model was appealingly precise and quick. On small projects, it worked well enough. But as projects grew, the text-editing model was going to lose steam. I wasn’t the only one to notice. Shortly after those million nerds made their first web page by hand, many of them set about devising ways to apply a programming model to web development.

3.3 “Now you have two problems”

What followed was a steady stream of products, frameworks, tools, and content management systems that claimed to bring a programming model to web development. Some were better than others. But none of them displaced the text editor as the preferred tool of web developers.

Why not? All these tools promised a great leap forward in solving the web-development problem. In practice, they simply redistributed the pain. I needn’t bore you with enumerating the deficiencies of specific tools, because they’ve tended to fail in the same thematic ways:

  • No native data structure for HTML. Core to any programming model is data structures. Good data structures make processing easy; bad ones make it hard. Even though HTML has a well documented format, rarely has it been handled within a programming system with a native, intuitive data structure. Instead, it’s either been treated as a string (wrong), a tree (also wrong), or some magical parsed object. This has made working with HTML in programming environments needlessly difficult.

  • Mandatory separation of code, presentation, and content. This principle has often been held out as an ideal in web development. But it’s also counterintuitive, because an HTML page naturally contains all three. If you want to separate them, your tools should let you. But if you don’t, your tools shouldn’t make you.

  • Compromised template languages. Seems like every programming language has at least 10 templating systems for HTML, all of which require you to learn a new “template language” that offers the worst of both worlds: fewer features and different syntax than the underlying language.

  • Steep learning curves. Web programmers have often chided designers for not knowing how to code. But programming-based web-development tools have often had a high initial learning curve that requires you to throw out your existing workflow. Programmers built these tools — no surprise that programmers have been more comfortable with them.

I’ve tried a lot of these tools over the years. Some I liked. Some I didn’t. Invariably, however, whenever I could still make do with hand-editing an HTML project, I would. After trying to cajole the web framework du jour into doing my bidding, it was relaxing to trade off some efficiency for control.

3.4 Rethinking the solution for digital books

In 2008, I launched a website called Typography for Lawyers. Initially, I’d conceived of it as a book. Then I thought “no one’s going to publish that.” So it became a website, that I aimed to make as book-like as possible. But hand-editing wasn’t going to be enough.

So I used WordPress. The major chore became scraping out all the crap that typically lives in blog templates. Largely because of this, people liked the site, because it was simpler & cleaner than the usual WordPress website.

Eventually, a publisher offered to release it as a paperback. Later came the inevitable request to make it into a Kindle book. As a fan of typography, I hate the Kindle. The layout controls are coarse, and so is the reading experience. But I didn’t run and hide. Basically a Kindle book is a little website made with 1995-era HTML. So I coded up some tools in Perl to convert my book to Kindle format while preserving the formatting and images as well as possible.

At that point, I noticed I had converted Typography for Lawyers into web format twice, using two different sets of tools. Before someone asked me to do it a third time, I started thinking about how I might create source code for the book that allowed me to render it into different formats.

This was the beginning of the Pollen project.

I wrote the initial version of Pollen in Python. I devised a simplified markup-notation language for the source files. This language was compiled into XML-ish data structures using ply (Python lex/yacc). These structures were parsed into trees using LXML. The trees were combined with templates made in Chameleon. These templates were rendered and previewed with the Bottle web server.

Did it work? Sort of. Source code went in; web pages came out. But it was also complicated and fragile. Moreover, though the automation was there, there wasn’t yet enough abstraction at the source layer. I started thinking about how I could add a source preprocessor.

3.5 Enter Racket

I had come across Racket while researching languages suitable for HTML/XML processing. I had unexpectedly learned about the secret kinship of XML and Lisp: though XML is not a programming language, it uses a variant of Lisp syntax. Thus Lisp languages are particularly adept at handling XMLish structures. That was interesting.

After comparing some of the Lisp & Scheme variants, Racket stood out because it had a text-based dialect called Scribble. Scribble could be used to embed code within textual content. That was interesting too. Among other things, this meant Scribble could be used as a general-purpose preprocessor. So I thought I’d see if I could add it to Pollen.

It worked. So well, in fact, that I started thinking about whether I could reimplement other parts of Pollen in Racket. Then I started thinking about reimplementing all of it in Racket.

So I did. And here we are.

3.6 What is Pollen?

Pollen is a publishing system built on top of Scribble and Racket. So far I’ve optimized Pollen for digital books, because that’s mainly what I use it for. But it can be used for small projects too.

As a publishing system, Pollen includes:

  • A programming language. The Pollen language is a variant of Scribble, with specific “dialects” tailored to different kinds of source files. You don’t need to use the programming features to do useful work, but they’re available when you need them.

  • A set of tools & libraries. Pollen targets HTML output. So it includes a variety of tools that cure common HTML annoyances, including a CSS preprocessor.

  • A development environment. Pollen works with the DrRacket IDE. It also includes a project web server so you can dynamically preview and revise your publication.

Pollen addresses the deficiencies I experienced with other tools:

  • Yes, we have a native data structure for HTML. Racket represents HTML structures as X-expressions, which are a variant of the standard Racket data structure, called S-expressions. In other words, not only is there a native representation for HTML, everything in the language is represented this way.

  • Flexible blending of code, presentation, and content. Pollen is a text-based language. So a Pollen source file might have no code at all. But as a dialect of Scribble & Racket, if you want to mix code with content, you can.

  • No template language. It’s not necessary, because you can use the whole Racket language, and all the usual Racket syntax, in every Pollen file.

  • Shallow learning curve. You don’t need to do a lot of setup and configuration to start doing useful work with Pollen. Programmers and non-programmers can easily collaborate. Yes, I concede that if you plan to get serious, you’ll need to learn some Racket. I don’t think you’ll regret it.

 
\ No newline at end of file +3 Backstory
6.1.0.5

3 Backstory

I created Pollen to overcome limitations & frustrations I repeatedly encountered with existing web-publishing tools.

If you agree with my characterization of those problems, then you’ll probably like the solution that Pollen offers. If not, you probably won’t.

3.1 Web development and its discontents

I made my first web page in 1994, shortly after the web was invented. I opened my text editor (at the time, BBEdit), pecked out <html><body>Hello world</body></html>, then loaded it in Mosaic. So did a million other nerds.

If you weren’t around then, you didn’t miss much. Everything about the web was horrible: the web browsers, the computers running the browsers, the dial-up connections feeding the browsers, and of course HTML itself. At that point, the desktop-software experience was already slick and refined. By comparison, using the web felt like banging rocks together.

That’s no longer true. The web is now 20 years old. During that time, most parts of the web have improved dramatically — for instance, the connections are faster, the browsers are more sophisticated, the screens have more pixels.

But one part has not improved: the way we make web pages. Over the years, tools promising to simplify HTML development have come and mostly gone — from PageMill to Dreamweaver to WordPress to Jekyll. Meanwhile, true web jocks have remained loyal to the original HTML power tool: the humble text editor.

In one way, this makes sense. Web pages are mostly made of text-based data — HTML, CSS, JavaScript, and so on — and the simplest way to mainpulate this data is with a text editor. While HTML and CSS are not programming languages, they lend themselves to semantic and logical structure that’s most easily expressed by editing them as text. Furthermore, text-based editing makes debugging and performance improvements easier.

But text-based editing is also limited. Though the underlying description of a web page is notionally human-readable, it’s largely optimized to be readable by other software — namely, web browsers. HTML markup in particular is verbose and easily mistyped. And isn’t it fatally dull to manage all the boilerplate, like surrounding every paragraph with <p>...</p>? Yes, it is.

For these reasons, much of web development should lend itself to abstraction & automation. Abstraction means consolidating repetitve, complex patterns into simpler, parameterized forms. Automation means avoiding the manual drudgery of generating the output files. But in practice, tools that enable this abstraction & automation have been slow to arrive, and most have come hobbled with unacceptable deficiencies.

3.2 The better idea: a programming model

Parallel with my HTML education, I also goofed around with various programming languages — C, C++, Perl, Java, PHP, JavaScript, Python. Unlike HTML, programming languages excel at abstraction and automation. This seemed like the obvious direction for web development to go.

What distinguishes the text-editing model from the programming model? It’s a matter of direct vs. indirect manipulation of output. The text-editing model treats HTML as something to be written directly with a text editor. Whereas the programming model treats HTML — or whatever the output is — as the result of compiling a set of source files, which are written in a programming language. The costs of working indirectly via the programming language are offset by the benefits of abstraction & automation.

On the early web, the text-editing model was appealingly precise and quick. On small projects, it worked well enough. But as projects grew, the text-editing model was going to lose steam. I wasn’t the only one to notice. Shortly after those million nerds made their first web page by hand, many of them set about devising ways to apply a programming model to web development.

3.3 “Now you have two problems”

What followed was a steady stream of products, frameworks, tools, and content management systems that claimed to bring a programming model to web development. Some were better than others. But none of them displaced the text editor as the preferred tool of web developers.

Why not? All these tools promised a great leap forward in solving the web-development problem. In practice, they simply redistributed the pain. I needn’t bore you with enumerating the deficiencies of specific tools, because they’ve tended to fail in the same thematic ways:

  • No native data structure for HTML. Core to any programming model is data structures. Good data structures make processing easy; bad ones make it hard. Even though HTML has a well documented format, rarely has it been handled within a programming system with a native, intuitive data structure. Instead, it’s either been treated as a string (wrong), a tree (also wrong), or some magical parsed object. This has made working with HTML in programming environments needlessly difficult.

  • Mandatory separation of code, presentation, and content. This principle has often been held out as an ideal in web development. But it’s also counterintuitive, because an HTML page naturally contains all three. If you want to separate them, your tools should let you. But if you don’t, your tools shouldn’t make you.

  • Compromised template languages. Seems like every programming language has at least 10 templating systems for HTML, all of which require you to learn a new “template language” that offers the worst of both worlds: fewer features and different syntax than the underlying language.

  • Steep learning curves. Web programmers have often chided designers for not knowing how to code. But programming-based web-development tools have often had a high initial learning curve that requires you to throw out your existing workflow. Programmers built these tools — no surprise that programmers have been more comfortable with them.

I’ve tried a lot of these tools over the years. Some I liked. Some I didn’t. Invariably, however, whenever I could still make do with hand-editing an HTML project, I would. After trying to cajole the web framework du jour into doing my bidding, it was relaxing to trade off some efficiency for control.

3.4 Rethinking the solution for digital books

In 2008, I launched a website called Typography for Lawyers. Initially, I’d conceived of it as a book. Then I thought “no one’s going to publish that.” So it became a website, that I aimed to make as book-like as possible. But hand-editing wasn’t going to be enough.

So I used WordPress. The major chore became scraping out all the crap that typically lives in blog templates. Largely because of this, people liked the site, because it was simpler & cleaner than the usual WordPress website.

Eventually, a publisher offered to release it as a paperback. Later came the inevitable request to make it into a Kindle book. As a fan of typography, I hate the Kindle. The layout controls are coarse, and so is the reading experience. But I didn’t run and hide. Basically a Kindle book is a little website made with 1995-era HTML. So I coded up some tools in Perl to convert my book to Kindle format while preserving the formatting and images as well as possible.

At that point, I noticed I had converted Typography for Lawyers into web format twice, using two different sets of tools. Before someone asked me to do it a third time, I started thinking about how I might create source code for the book that allowed me to render it into different formats.

This was the beginning of the Pollen project.

I wrote the initial version of Pollen in Python. I devised a simplified markup-notation language for the source files. This language was compiled into XML-ish data structures using ply (Python lex/yacc). These structures were parsed into trees using LXML. The trees were combined with templates made in Chameleon. These templates were rendered and previewed with the Bottle web server.

Did it work? Sort of. Source code went in; web pages came out. But it was also complicated and fragile. Moreover, though the automation was there, there wasn’t yet enough abstraction at the source layer. I started thinking about how I could add a source preprocessor.

3.5 Enter Racket

I had come across Racket while researching languages suitable for HTML/XML processing. I had unexpectedly learned about the secret kinship of XML and Lisp: though XML is not a programming language, it uses a variant of Lisp syntax. Thus Lisp languages are particularly adept at handling XMLish structures. That was interesting.

After comparing some of the Lisp & Scheme variants, Racket stood out because it had a text-based dialect called Scribble. Scribble could be used to embed code within textual content. That was interesting too. Among other things, this meant Scribble could be used as a general-purpose preprocessor. So I thought I’d see if I could add it to Pollen.

It worked. So well, in fact, that I started thinking about whether I could reimplement other parts of Pollen in Racket. Then I started thinking about reimplementing all of it in Racket.

So I did. And here we are.

3.6 What is Pollen?

Pollen is a publishing system built on top of Scribble and Racket. So far I’ve optimized Pollen for digital books, because that’s mainly what I use it for. But it can be used for small projects too.

As a publishing system, Pollen includes:

  • A programming language. The Pollen language is a variant of Scribble, with specific “dialects” tailored to different kinds of source files. You don’t need to use the programming features to do useful work, but they’re available when you need them.

  • A set of tools & libraries. Pollen targets HTML output. So it includes a variety of tools that cure common HTML annoyances, including a CSS preprocessor.

  • A development environment. Pollen works with the DrRacket IDE. It also includes a project web server so you can dynamically preview and revise your publication.

Pollen addresses the deficiencies I experienced with other tools:

  • Yes, we have a native data structure for HTML. Racket represents HTML structures as X-expressions, which are a variant of the standard Racket data structure, called S-expressions. In other words, not only is there a native representation for HTML, everything in the language is represented this way.

  • Flexible blending of code, presentation, and content. Pollen is a text-based language. So a Pollen source file might have no code at all. But as a dialect of Scribble & Racket, if you want to mix code with content, you can.

  • No template language. It’s not necessary, because you can use the whole Racket language, and all the usual Racket syntax, in every Pollen file.

  • Shallow learning curve. You don’t need to do a lot of setup and configuration to start doing useful work with Pollen. Programmers and non-programmers can easily collaborate. Yes, I concede that if you plan to get serious, you’ll need to learn some Racket. I don’t think you’ll regret it.

 
\ No newline at end of file diff --git a/doc/Cache.html b/doc/Cache.html index f5f15c4..567902d 100644 --- a/doc/Cache.html +++ b/doc/Cache.html @@ -1,2 +1,2 @@ -10.1 Cache
6.1.0.5

10.1 Cache

 (require pollen/cache) package: pollen

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

parameter

(current-cache)  hash?

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

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

procedure

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

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

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

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

procedure

(make-cache)  hash?

Initializes current-cache.

procedure

(reset-cache)  void?

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

procedure

(cache-ref source-path)  hash?

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

 
\ No newline at end of file +11.1 Cache
6.1.0.5

11.1 Cache

 (require pollen/cache) package: pollen

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

parameter

(current-cache)  hash?

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

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

procedure

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

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

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

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

procedure

(make-cache)  hash?

Initializes current-cache.

procedure

(reset-cache)  void?

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

procedure

(cache-ref source-path)  hash?

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

 
\ No newline at end of file diff --git a/doc/Decode.html b/doc/Decode.html index 48dc1fb..f3bce2d 100644 --- a/doc/Decode.html +++ b/doc/Decode.html @@ -1,2 +1,2 @@ -10.2 Decode
6.1.0.5

10.2 Decode

 (require pollen/decode) package: pollen

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

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

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

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

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

procedure

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

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

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

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

(define (root . items)
  (decode (make-txexpr 'root '() items)
          #:txexpr-elements-proc detect-paragraphs
          #:block-txexpr-proc (compose1 hyphenate wrap-hanging-quotes)
          #:string-proc (compose1 smart-quotes smart-dashes)
          #:exclude-tags '(style script)))

The hyphenate function is not part of Pollen, but rather the hyphenate package, which you can install separately.

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

Examples:

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

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

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

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

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

Examples:

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

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

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

Examples:

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

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

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

Examples:

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

'(div

  ((class "PhD"))

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

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

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

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

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

Examples:

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

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

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

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

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

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

Examples:

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

'(body "The first paragraph.")

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

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

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

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

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

Examples:

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

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

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

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

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

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

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

Examples:

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

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

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

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

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

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

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

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

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

Examples:

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

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

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

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

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

Examples:

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

'(body

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

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

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

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

'(body

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

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

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

10.2.1 Block

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

parameter

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

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

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

procedure

(register-block-tag tag)  void?

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

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

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

Examples:

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

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

; Wrong: bloq should not be wrapped

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

Examples:

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

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

; Right: bloq is treated as a block

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

Examples:

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

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

procedure

(block-txexpr? v)  boolean?

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

10.2.2 Typography

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

procedure

(whitespace? v)  boolean?

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

Examples:

> (whitespace? "\n\n   ")

#t

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

#t

> (whitespace? "")

#t

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

#t

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

#f

procedure

(whitespace/nbsp? v)  boolean?

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

Examples:

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

#t

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

#t

> (whitespace/nbsp? "")

#t

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

#t

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

#t

procedure

(smart-quotes str)  string?

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

Examples:

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

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

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

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

procedure

(smart-dashes str)  string?

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

Examples:

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

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

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

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

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

procedure

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

Examples:

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

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

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

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

procedure

(detect-paragraphs elements 
  [#:separator paragraph-sep 
  #:tag paragraph-tag 
  #:linebreak-proc linebreak-proc]) 
  txexpr-elements?
  elements : txexpr-elements?
  paragraph-sep : string? = world:paragraph-separator
  paragraph-tag : symbol? = 'p
  linebreak-proc : (txexpr-elements? . -> . txexpr-elements?)
   = detect-linebreaks
Find paragraphs within elements (as denoted by paragraph-sep) and wrap them with paragraph-tag. Also handle linebreaks using detect-linebreaks.

If element is already a block-txexpr?, it will not be wrapped as a paragraph (because in that case, the wrapping would be superfluous). Thus, as a consequence, if paragraph-sep occurs between two blocks, it will be ignored (as in the example below using two sequential 'div blocks.)

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

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

Examples:

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

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

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

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

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

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

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

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

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

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

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

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

procedure

(wrap-hanging-quotes tx 
  [#:single-preprend single-preprender 
  #:double-preprend double-preprender]) 
  txexpr?
  tx : txexpr?
  single-preprender : txexpr-tag? = 'squo
  double-preprender : txexpr-tag? = 'dquo
Find single or double quote marks at the beginning of tx and wrap them in an X-expression with the tag single-preprender or double-preprender, respectively. The default values are 'squo and 'dquo.

Examples:

> (wrap-hanging-quotes '(p "No quote to hang."))

'(p "No quote to hang.")

> (wrap-hanging-quotes '(p "“What? We need to hang quotes?”"))

'(p (dquo "“" "What? We need to hang quotes?”"))

In pro typography, quotation marks at the beginning of a line or paragraph are often shifted into the margin slightly to make them appear more optically aligned with the left edge of the text. With a reflowable layout model like HTML, you don’t know where your line breaks will be.

This function will simply insert the 'squo and 'dquo tags, which provide hooks that let you do the actual hanging via CSS, like so (actual measurement can be refined to taste):

squo {margin-left: -0.25em;}

dquo {margin-left: -0.50em;}

Be warned: there are many edge cases this function does not handle well.

Examples:

; Argh: this edge case is not handled properly
> (wrap-hanging-quotes '(p "“" (em "What?") "We need to hang quotes?”"))

'(p "“" (em "What?") "We need to hang quotes?”")

 
\ No newline at end of file +11.2 Decode
6.1.0.5

11.2 Decode

 (require pollen/decode) package: pollen

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

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

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

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

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

procedure

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

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

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

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

(define (root . items)
  (decode (make-txexpr 'root '() items)
          #:txexpr-elements-proc detect-paragraphs
          #:block-txexpr-proc (compose1 hyphenate wrap-hanging-quotes)
          #:string-proc (compose1 smart-quotes smart-dashes)
          #:exclude-tags '(style script)))

The hyphenate function is not part of Pollen, but rather the hyphenate package, which you can install separately.

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

Examples:

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

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

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

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

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

Examples:

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

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

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

Examples:

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

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

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

Examples:

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

'(div

  ((class "PhD"))

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

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

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

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

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

Examples:

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

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

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

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

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

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

Examples:

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

'(body "The first paragraph.")

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

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

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

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

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

Examples:

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

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

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

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

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

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

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

Examples:

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

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

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

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

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

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

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

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

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

Examples:

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

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

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

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

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

Examples:

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

'(body

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

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

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

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

'(body

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

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

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

11.2.1 Block

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

parameter

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

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

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

procedure

(register-block-tag tag)  void?

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

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

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

Examples:

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

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

; Wrong: bloq should not be wrapped

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

Examples:

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

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

; Right: bloq is treated as a block

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

Examples:

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

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

procedure

(block-txexpr? v)  boolean?

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

11.2.2 Typography

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

procedure

(whitespace? v)  boolean?

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

Examples:

> (whitespace? "\n\n   ")

#t

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

#t

> (whitespace? "")

#t

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

#t

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

#f

procedure

(whitespace/nbsp? v)  boolean?

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

Examples:

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

#t

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

#t

> (whitespace/nbsp? "")

#t

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

#t

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

#t

procedure

(smart-quotes str)  string?

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

Examples:

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

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

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

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

procedure

(smart-dashes str)  string?

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

Examples:

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

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

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

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

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

procedure

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

Examples:

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

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

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

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

procedure

(detect-paragraphs elements 
  [#:separator paragraph-sep 
  #:tag paragraph-tag 
  #:linebreak-proc linebreak-proc]) 
  txexpr-elements?
  elements : txexpr-elements?
  paragraph-sep : string? = world:paragraph-separator
  paragraph-tag : symbol? = 'p
  linebreak-proc : (txexpr-elements? . -> . txexpr-elements?)
   = detect-linebreaks
Find paragraphs within elements (as denoted by paragraph-sep) and wrap them with paragraph-tag. Also handle linebreaks using detect-linebreaks.

If element is already a block-txexpr?, it will not be wrapped as a paragraph (because in that case, the wrapping would be superfluous). Thus, as a consequence, if paragraph-sep occurs between two blocks, it will be ignored (as in the example below using two sequential 'div blocks.)

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

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

Examples:

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

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

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

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

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

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

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

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

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

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

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

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

procedure

(wrap-hanging-quotes tx 
  [#:single-preprend single-preprender 
  #:double-preprend double-preprender]) 
  txexpr?
  tx : txexpr?
  single-preprender : txexpr-tag? = 'squo
  double-preprender : txexpr-tag? = 'dquo
Find single or double quote marks at the beginning of tx and wrap them in an X-expression with the tag single-preprender or double-preprender, respectively. The default values are 'squo and 'dquo.

Examples:

> (wrap-hanging-quotes '(p "No quote to hang."))

'(p "No quote to hang.")

> (wrap-hanging-quotes '(p "“What? We need to hang quotes?”"))

'(p (dquo "“" "What? We need to hang quotes?”"))

In pro typography, quotation marks at the beginning of a line or paragraph are often shifted into the margin slightly to make them appear more optically aligned with the left edge of the text. With a reflowable layout model like HTML, you don’t know where your line breaks will be.

This function will simply insert the 'squo and 'dquo tags, which provide hooks that let you do the actual hanging via CSS, like so (actual measurement can be refined to taste):

squo {margin-left: -0.25em;}

dquo {margin-left: -0.50em;}

Be warned: there are many edge cases this function does not handle well.

Examples:

; Argh: this edge case is not handled properly
> (wrap-hanging-quotes '(p "“" (em "What?") "We need to hang quotes?”"))

'(p "“" (em "What?") "We need to hang quotes?”")

 
\ No newline at end of file diff --git a/doc/File_formats.html b/doc/File_formats.html index b933cac..446485e 100644 --- a/doc/File_formats.html +++ b/doc/File_formats.html @@ -1,2 +1,2 @@ -8 File formats
6.1.0.5

8 File formats

8.1 Source formats

 #lang pollen/pre package: pollen
 #lang pollen/markdown
 #lang pollen/markup
 #lang pollen/ptree

The Pollen language is divided into variants, or dialects, that are tailored to suit each of the core source formats.

These dialects can be invoked one of two ways: either by invoking a specific dialect in the first line of the file (also known as the #lang line), or by using the generic #lang pollen as the first line, and then the correct dialect will be automatically selected based on the source file extension.

If the #lang line specifies a dialect different from the one specified by the file extension, the #lang line will take precedence.

For ease of use, the behavior of the Pollen language departs from the standard Racket language in several ways. The differences are noted below.

8.1.1 Command syntax using ◊

Commands must start with the special lozenge character . Other material is interpreted as plain text. See ◊ command overview for more.

How is this different from Racket? In Racket, everything is a command, and plain text must be quoted.

8.1.2 Any command is valid

There are no undefined commands in Pollen. If a command has not already been defined, it’s treated as a tag function. See ◊ command overview for more.

How is this different from Racket? In Racket, if you try to treat an identifier as a function before defining it with define, you’ll get an error.

8.1.3 Standard exports

By default, every Pollen source file exports two symbols, which you can access by using the source file with require:

'doc contains the output of the file. The type of output depends on the source format (documented below).

'metas is a hash of key–value pairs with extra information that is extracted from the source. These 'metas will always contain the key 'here-path, which returns a string representation of the full path to the source file. Beyond that, the only 'metas are the ones that are specified within the source file (see the source formats below for more detail on how to specify metas).

How is this different from Racket? In Racket, you must explicitly define and then provide any values you want to export.

8.1.4 Custom exports

Any value or function that is defined within the source file using define is automatically exported.

How is this different from Racket? In Racket, you must explicitly provide any values you want to export. Unlike Racket, every Pollen source file impliedly uses (provide (all-defined-out)).

8.1.5 The project-require.rkt file

If a file called project-require.rkt exists in the same directory with a source file, it’s automatically imported when the source file is compiled.

How is this different from Racket? In Racket, you must explicitly import files using require.

8.1.6 Preprocessor (.pp extension)

Invoke the preprocessor dialect by using #lang pollen/pre as the first line of your source file, or by using #lang pollen with a file extension of .pp. These forms are equivalent:

"sample.css.pp"

#lang pollen
...source...

"sample.css"

#lang pollen/pre
...source...

When no dialect is explicitly specified by either the #lang line or the file extension, Pollen will default to using the preprocessor dialect. For instance, this file will be treated as preprocessor source:

"test.yyz"

#lang pollen
...source...

Of course, you’re better off specifying the preprocessor dialect explicitly rather than relying on this default behavior.

The output of the preprocessor dialect, provided by 'doc, is plain text.

8.1.7 Markdown (.pmd extension)

Invoke the Markdown dialect by using #lang pollen/markdown as the first line of your source file, or by using #lang pollen with a file extension of .pmd. These forms are equivalent:

"sample.txt.pmd"

#lang pollen
...source...

"sample.txt"

#lang pollen/markdown
...source...

The output of the Markdown dialect, provided by 'doc, is a tagged X-expression.

8.1.8 Markup (.pm extension)

Invoke the Pollen markup dialect by using #lang pollen/markup as the first line of your source file, or by using #lang pollen with a file extension of .pm. These forms are equivalent:

"about.html.pm"

#lang pollen
...source...

"about.html"

#lang pollen/markup
...source...

The output of the Pollen markup dialect, provided by 'doc, is a tagged X-expression.

8.1.9 Pagetree (.ptree extension)

Invoke the pagetree dialect by using #lang pollen/ptree as the first line of your source file, or by using #lang pollen with a file extension of .ptree. These forms are equivalent:

"main.ptree"

#lang pollen
...source...

"main.rkt"

#lang pollen/ptree
...source...

The output of the pagetree dialect, provided by 'doc, is a pagetree? that is checked for correctness using validate-pagetree.

8.2 Utility formats

These aren’t source formats because they don’t contain a #lang pollen line. But for convenience, they get special handling by the Pollen project server.

8.2.1 Scribble (.scrbl extension)

Scribble files are recognized by the project server and can be compiled and previewed in single-page mode.

8.2.2 Null (.p extension)

Files with the null extension are simply rendered as a copy of the file without the extension, so index.html.p becomes index.html.

This can be useful you’re managing your project with git. Most likely you’ll want to ignore *.html and other file types that are frequently regenerated by the project server. But if you have isolated static files — for instance, a 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.

 
\ No newline at end of file +9 File formats
6.1.0.5

9 File formats

9.1 Source formats

 #lang pollen/pre package: pollen
 #lang pollen/markdown
 #lang pollen/markup
 #lang pollen/ptree

The Pollen language is divided into variants, or dialects, that are tailored to suit each of the core source formats.

These dialects can be invoked one of two ways: either by invoking a specific dialect in the first line of the file (also known as the #lang line), or by using the generic #lang pollen as the first line, and then the correct dialect will be automatically selected based on the source file extension.

If the #lang line specifies a dialect different from the one specified by the file extension, the #lang line will take precedence.

For ease of use, the behavior of the Pollen language departs from the standard Racket language in several ways. The differences are noted below.

9.1.1 Command syntax using ◊

Commands must start with the special lozenge character . Other material is interpreted as plain text. See ◊ command overview for more.

How is this different from Racket? In Racket, everything is a command, and plain text must be quoted.

9.1.2 Any command is valid

There are no undefined commands in Pollen. If a command has not already been defined, it’s treated as a tag function. See ◊ command overview for more.

How is this different from Racket? In Racket, if you try to treat an identifier as a function before defining it with define, you’ll get an error.

9.1.3 Standard exports

By default, every Pollen source file exports two symbols, which you can access by using the source file with require:

'doc contains the output of the file. The type of output depends on the source format (documented below).

'metas is a hash of key–value pairs with extra information that is extracted from the source. These 'metas will always contain the key 'here-path, which returns a string representation of the full path to the source file. Beyond that, the only 'metas are the ones that are specified within the source file (see the source formats below for more detail on how to specify metas).

How is this different from Racket? In Racket, you must explicitly define and then provide any values you want to export.

9.1.4 Custom exports

Any value or function that is defined within the source file using define is automatically exported.

How is this different from Racket? In Racket, you must explicitly provide any values you want to export. Unlike Racket, every Pollen source file impliedly uses (provide (all-defined-out)).

9.1.5 The directory-require.rkt file

If a file called directory-require.rkt exists in the same directory with a source file, it’s automatically imported when the source file is compiled.

How is this different from Racket? In Racket, you must explicitly import files using require.

9.1.6 Preprocessor (.pp extension)

Invoke the preprocessor dialect by using #lang pollen/pre as the first line of your source file, or by using #lang pollen with a file extension of .pp. These forms are equivalent:

"sample.css.pp"

#lang pollen
...source...

"sample.css"

#lang pollen/pre
...source...

When no dialect is explicitly specified by either the #lang line or the file extension, Pollen will default to using the preprocessor dialect. For instance, this file will be treated as preprocessor source:

"test.yyz"

#lang pollen
...source...

Of course, you’re better off specifying the preprocessor dialect explicitly rather than relying on this default behavior.

The output of the preprocessor dialect, provided by 'doc, is plain text.

9.1.7 Markdown (.pmd extension)

Invoke the Markdown dialect by using #lang pollen/markdown as the first line of your source file, or by using #lang pollen with a file extension of .pmd. These forms are equivalent:

"sample.txt.pmd"

#lang pollen
...source...

"sample.txt"

#lang pollen/markdown
...source...

The output of the Markdown dialect, provided by 'doc, is a tagged X-expression.

9.1.8 Markup (.pm extension)

Invoke the Pollen markup dialect by using #lang pollen/markup as the first line of your source file, or by using #lang pollen with a file extension of .pm. These forms are equivalent:

"about.html.pm"

#lang pollen
...source...

"about.html"

#lang pollen/markup
...source...

The output of the Pollen markup dialect, provided by 'doc, is a tagged X-expression.

9.1.9 Pagetree (.ptree extension)

Invoke the pagetree dialect by using #lang pollen/ptree as the first line of your source file, or by using #lang pollen with a file extension of .ptree. These forms are equivalent:

"main.ptree"

#lang pollen
...source...

"main.rkt"

#lang pollen/ptree
...source...

The output of the pagetree dialect, provided by 'doc, is a pagetree? that is checked for correctness using validate-pagetree.

9.2 Utility formats

These aren’t source formats because they don’t contain a #lang pollen line. But for convenience, they get special handling by the Pollen project server.

9.2.1 Scribble (.scrbl extension)

Scribble files are recognized by the project server and can be compiled and previewed in single-page mode.

9.2.2 Null (.p extension)

Files with the null extension are simply rendered as a copy of the file without the extension, so index.html.p becomes index.html.

This can be useful you’re managing your project with git. Most likely you’ll want to ignore *.html and other file types that are frequently regenerated by the project server. But if you have isolated static files — for instance, a 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.

 
\ No newline at end of file diff --git a/doc/Installation.html b/doc/Installation.html index d8a713d..8cccfa1 100644 --- a/doc/Installation.html +++ b/doc/Installation.html @@ -1,4 +1,4 @@ -1 Installation
6.1.0.5

1 Installation

Install Racket, which includes DrRacket.

From the command line, install Pollen: +1 Installation
6.1.0.5

1 Installation

Install Racket, which includes DrRacket.

From the command line, install Pollen:

raco pkg install pollen

After that, you can update the package from the command line:

raco pkg update pollen

 
\ No newline at end of file diff --git a/doc/License___source_code.html b/doc/License___source_code.html index 58e9cd8..44be73f 100644 --- a/doc/License___source_code.html +++ b/doc/License___source_code.html @@ -1,2 +1,2 @@ -12 License & source code
6.1.0.5

12 License & source code

This module is licensed under the LGPL.

Source repository at http://github.com/mbutterick/pollen. Suggestions & corrections welcome.

 
\ No newline at end of file +13 License & source code
6.1.0.5

13 License & source code

This module is licensed under the LGPL.

Source repository at http://github.com/mbutterick/pollen. Suggestions & corrections welcome.

 
\ No newline at end of file diff --git a/doc/Module_reference.html b/doc/Module_reference.html index 9f78e6a..fa0e418 100644 --- a/doc/Module_reference.html +++ b/doc/Module_reference.html @@ -1,2 +1,2 @@ -10 Module reference
6.1.0.5

10 Module reference

    10.1 Cache

    10.2 Decode

      10.2.1 Block

      10.2.2 Typography

    10.3 File

    10.4 Pagetree

      10.4.1 Navigation

      10.4.2 Utilities

    10.5 Render

    10.6 Template

    10.7 Tag

    10.8 Top

    10.9 World

 
\ No newline at end of file +11 Module reference
6.1.0.5

11 Module reference

    11.1 Cache

    11.2 Decode

      11.2.1 Block

      11.2.2 Typography

    11.3 File

    11.4 Pagetree

      11.4.1 Navigation

      11.4.2 Utilities

    11.5 Render

    11.6 Template

    11.7 Tag

    11.8 Top

    11.9 World

 
\ No newline at end of file diff --git a/doc/Pagetree.html b/doc/Pagetree.html index c82f27d..8974b13 100644 --- a/doc/Pagetree.html +++ b/doc/Pagetree.html @@ -1,2 +1,2 @@ -10.4 Pagetree
6.1.0.5

10.4 Pagetree

 (require pollen/pagetree) package: pollen

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

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

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

procedure

(pagetree? possible-pagetree)  boolean?

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

Examples:

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

#t

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

#f

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

#f

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

#t

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

#t

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

#f

procedure

(validate-pagetree possible-pagetree)  pagetree?

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

Examples:

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

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

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

#f

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

validate-pagetree: members-unique? failed because items

aren’t unique: (son.html mama.html)

procedure

(pagenode? possible-pagenode)  boolean?

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

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

Examples:

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

'(#t #t #t)

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

'(#f #f #f #f)

procedure

(pagenodeish? v)  boolean?

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

Example:

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

'(#t #t #f)

procedure

(->pagenode v)  pagenode?

  v : pagenodeish?
Convert v to a pagenode.

Examples:

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

'(#t #t #t #t)

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

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

10.4.1 Navigation

parameter

(current-pagetree)  pagetree?

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

procedure

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

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

Examples:

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

'mama.html

> (parent "mama.html")

'root

> (parent (parent 'son.html))

'root

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

#f

procedure

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

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

Examples:

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

'(son.html daughter.html)

> (children 'uncle.html)

#f

> (children 'root)

'(mama.html uncle.html)

> (map children (children 'root))

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

procedure

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

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

Examples:

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

'(son.html daughter.html)

> (siblings 'daughter.html)

'(son.html daughter.html)

> (siblings 'mama.html)

'(mama.html uncle.html)

procedure

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

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

procedure

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

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

Examples:

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

'son.html

> (previous 'son.html)

'mama.html

> (previous (previous 'daughter.html))

'mama.html

> (previous 'mama.html)

#f

> (previous* 'daughter.html)

'(mama.html son.html)

> (previous* 'uncle.html)

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

procedure

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

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

procedure

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

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

Examples:

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

'daughter.html

> (next 'daughter.html)

'uncle.html

> (next (next 'son.html))

'uncle.html

> (next 'uncle.html)

#f

> (next* 'mama.html)

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

> (next* 'daughter.html)

'(uncle.html)

10.4.2 Utilities

procedure

(pagetree->list pagetree)  list?

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

procedure

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

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

procedure

(path->pagenode p)  pagenode?

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

 
\ No newline at end of file +11.4 Pagetree
6.1.0.5

11.4 Pagetree

 (require pollen/pagetree) package: pollen

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

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

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

procedure

(pagetree? possible-pagetree)  boolean?

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

Examples:

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

#t

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

#f

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

#f

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

#t

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

#t

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

#f

procedure

(validate-pagetree possible-pagetree)  pagetree?

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

Examples:

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

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

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

#f

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

validate-pagetree: members-unique? failed because items

aren’t unique: (mama.html son.html)

procedure

(pagenode? possible-pagenode)  boolean?

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

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

Examples:

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

'(#t #t #t)

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

'(#f #f #f #f)

procedure

(pagenodeish? v)  boolean?

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

Example:

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

'(#t #t #f)

procedure

(->pagenode v)  pagenode?

  v : pagenodeish?
Convert v to a pagenode.

Examples:

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

'(#t #t #t #t)

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

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

11.4.1 Navigation

parameter

(current-pagetree)  pagetree?

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

procedure

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

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

Examples:

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

'mama.html

> (parent "mama.html")

'root

> (parent (parent 'son.html))

'root

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

#f

procedure

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

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

Examples:

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

'(son.html daughter.html)

> (children 'uncle.html)

#f

> (children 'root)

'(mama.html uncle.html)

> (map children (children 'root))

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

procedure

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

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

Examples:

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

'(son.html daughter.html)

> (siblings 'daughter.html)

'(son.html daughter.html)

> (siblings 'mama.html)

'(mama.html uncle.html)

procedure

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

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

procedure

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

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

Examples:

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

'son.html

> (previous 'son.html)

'mama.html

> (previous (previous 'daughter.html))

'mama.html

> (previous 'mama.html)

#f

> (previous* 'daughter.html)

'(mama.html son.html)

> (previous* 'uncle.html)

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

procedure

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

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

procedure

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

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

Examples:

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

'daughter.html

> (next 'daughter.html)

'uncle.html

> (next (next 'son.html))

'uncle.html

> (next 'uncle.html)

#f

> (next* 'mama.html)

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

> (next* 'daughter.html)

'(uncle.html)

11.4.2 Utilities

procedure

(pagetree->list pagetree)  list?

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

procedure

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

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

procedure

(path->pagenode p)  pagenode?

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

 
\ No newline at end of file diff --git a/doc/Render.html b/doc/Render.html index b816920..42e5843 100644 --- a/doc/Render.html +++ b/doc/Render.html @@ -1,4 +1,4 @@ -10.5 Render
6.1.0.5

10.5 Render

 (require pollen/render) package: pollen

Rendering is how Pollen source files get converted into output.

procedure

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

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

A [pollen/pre] file is rendered without a template.

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

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

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

procedure

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

procedure

(render-to-file-if-needed source-path    
  [template-path    
  output-path    
  #:force force-render?])  void?
  source-path : complete-path?
  template-path : (or/c #f complete-path?) = #f
  output-path : (or/c #f complete-path?) = #f
  force-render? : boolean? = #f
Like render-to-file, but the render only happens if one of these conditions exist: +11.5 Render
6.1.0.5

11.5 Render

 (require pollen/render) package: pollen

Rendering is how Pollen source files get converted into output.

procedure

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

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

A [pollen/pre] file is rendered without a template.

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

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

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

procedure

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

procedure

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

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

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

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

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

procedure

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

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

procedure

(render-pagetree pagetree)  void?

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

procedure

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

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

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

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

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

 
\ No newline at end of file +
  1. If the metas for source-path have a key for template, then use the value of this key.

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

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

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

 
\ No newline at end of file diff --git a/doc/Tag.html b/doc/Tag.html index 84e77c5..d9b778b 100644 --- a/doc/Tag.html +++ b/doc/Tag.html @@ -1,2 +1,2 @@ -10.7 Tag
6.1.0.5

10.7 Tag

 (require pollen/tag) package: pollen

Convenience functions for working with tags.

procedure

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

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

Examples:

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

'(em "Bonjour")

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

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

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

Examples:

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

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

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

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

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

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

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

 
\ No newline at end of file +11.7 Tag
6.1.0.5

11.7 Tag

 (require pollen/tag) package: pollen

Convenience functions for working with tags.

procedure

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

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

Examples:

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

'(em "Bonjour")

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

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

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

Examples:

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

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

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

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

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

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

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

procedure

(split-attributes parts)  
txexpr-attrs? txexpr-elements?
  parts : list?
Helper function for custom tag functions. Take a rest argument that possibly includes tag attributes plus elements, and split it into attributes and elements. If there are no attributes, that return value will be the empty list. Properly parses the abbreviated Pollen syntax for attributes (described in make-default-tag-function).

Examples:

> (require pollen/tag)
> (define (tag . parts)
    (define-values (attrs elements) (split-attributes parts))
    (values attrs elements))
> (tag "Hello world")

'()

'("Hello world")

> (tag '((key "value")) "Hello world")

'((key "value"))

'("Hello world")

> (tag 'key: "value" "Hello world")

'((key "value"))

'("Hello world")

 
\ No newline at end of file diff --git a/doc/Template.html b/doc/Template.html index aaec3da..9589a80 100644 --- a/doc/Template.html +++ b/doc/Template.html @@ -1,2 +1,2 @@ -10.6 Template
6.1.0.5

10.6 Template

 (require pollen/template) package: pollen

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

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

procedure

(->html xexpr    
  [#:tag html-tag    
  #:attrs html-attrs    
  #:splice splice-html?])  string?
  xexpr : xexpr?
  html-tag : (or/c #f txexpr-tag?) = #f
  html-attrs : (or/c #f txexpr-attrs?) = #f
  splice-html? : boolean? = #f
Convert xexpr to an HTML string. Similar to xexpr->string, but consistent with the HTML spec, text that appears within script or style blocks will not be escaped.

Examples:

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

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

> (->html tx)

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

The optional keyword arguments html-tag and html-attrs let you set the outer tag and attributes for the generated HTML. If xexpr already has an outer tag or attributes, they will be replaced.

Examples:

> (define tx '(root ((id "huff")) "Bunk beds"))
> (->html tx)

"<root id=\"huff\">Bunk beds</root>"

> (->html tx #:tag 'div)

"<div id=\"huff\">Bunk beds</div>"

> (->html tx #:attrs '((id "doback")))

"<root id=\"doback\">Bunk beds</root>"

> (->html tx #:tag 'div #:attrs '((id "doback")))

"<div id=\"doback\">Bunk beds</div>"

Whereas if xexpr has no tag or attributes, they will be added. If you supply attributes without a tag, you’ll get an error.

Examples:

> (define x "Drum kit")
> (->html x)

"Drum kit"

> (->html x #:tag 'div)

"<div>Drum kit</div>"

> (->html x #:tag 'div #:attrs '((id "doback")))

"<div id=\"doback\">Drum kit</div>"

> (->html x #:attrs '((id "doback")))

->html: can't use attribute list '((id doback)) without a

#:tag argument

If the generated HTML has an outer tag, the splice-html? option will strip it off. Otherwise this option has no effect.

Examples:

> (define tx '(root (p "Chicken nuggets")))
> (->html tx)

"<root><p>Chicken nuggets</p></root>"

> (->html tx #:splice #t)

"<p>Chicken nuggets</p>"

> (define x "Fancy sauce")
> (->html x)

"Fancy sauce"

; This next one won't do anything
> (->html x #:splice #t)

"Fancy sauce"

; Adds the outer tag, but then #:splice removes it
> (->html x #:tag 'div #:attrs '((id "doback")) #:splice #t)

"Fancy sauce"

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

Examples:

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

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

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

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

procedure

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

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

procedure

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

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

procedure

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

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

Examples:

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

"sub.xml.pt"

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

"print"

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

#f

procedure

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

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

Examples:

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

'("Flavor?")

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

'("Nocciola" "Pistachio")

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

#f

procedure

(when/block condition text-to-insert)  string?

  condition : any/c
  text-to-insert : any/c
Convenience function for templates that’s simpler to use than plain when. If condition is true, then put the text-to-insert into the template at the current location. Within a template file, usually invoked like so:

◊when/block[condition]{The text to insert.}

The inserted text can contain its own nested Pollen commands.

 
\ No newline at end of file +11.6 Template
6.1.0.5

11.6 Template

 (require pollen/template) package: pollen

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

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

procedure

(->html xexpr    
  [#:tag html-tag    
  #:attrs html-attrs    
  #:splice splice-html?])  string?
  xexpr : xexpr?
  html-tag : (or/c #f txexpr-tag?) = #f
  html-attrs : (or/c #f txexpr-attrs?) = #f
  splice-html? : boolean? = #f
Convert xexpr to an HTML string. Similar to xexpr->string, but consistent with the HTML spec, text that appears within script or style blocks will not be escaped.

Examples:

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

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

> (->html tx)

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

The optional keyword arguments html-tag and html-attrs let you set the outer tag and attributes for the generated HTML. If xexpr already has an outer tag or attributes, they will be replaced.

Examples:

> (define tx '(root ((id "huff")) "Bunk beds"))
> (->html tx)

"<root id=\"huff\">Bunk beds</root>"

> (->html tx #:tag 'div)

"<div id=\"huff\">Bunk beds</div>"

> (->html tx #:attrs '((id "doback")))

"<root id=\"doback\">Bunk beds</root>"

> (->html tx #:tag 'div #:attrs '((id "doback")))

"<div id=\"doback\">Bunk beds</div>"

Whereas if xexpr has no tag or attributes, they will be added. If you supply attributes without a tag, you’ll get an error.

Examples:

> (define x "Drum kit")
> (->html x)

"Drum kit"

> (->html x #:tag 'div)

"<div>Drum kit</div>"

> (->html x #:tag 'div #:attrs '((id "doback")))

"<div id=\"doback\">Drum kit</div>"

> (->html x #:attrs '((id "doback")))

->html: can't use attribute list '((id doback)) without a

#:tag argument

If the generated HTML has an outer tag, the splice-html? option will strip it off. Otherwise this option has no effect.

Examples:

> (define tx '(root (p "Chicken nuggets")))
> (->html tx)

"<root><p>Chicken nuggets</p></root>"

> (->html tx #:splice #t)

"<p>Chicken nuggets</p>"

> (define x "Fancy sauce")
> (->html x)

"Fancy sauce"

; This next one won't do anything
> (->html x #:splice #t)

"Fancy sauce"

; Adds the outer tag, but then #:splice removes it
> (->html x #:tag 'div #:attrs '((id "doback")) #:splice #t)

"Fancy sauce"

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

Examples:

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

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

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

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

procedure

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

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

procedure

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

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

procedure

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

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

Examples:

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

"sub.xml.pt"

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

"print"

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

#f

procedure

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

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

Examples:

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

'("Flavor?")

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

'("Nocciola" "Pistachio")

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

#f

procedure

(when/block condition text-to-insert)  string?

  condition : any/c
  text-to-insert : any/c
Convenience function for templates that’s simpler to use than plain when. If condition is true, then put the text-to-insert into the template at the current location. Within a template file, usually invoked like so:

◊when/block[condition]{The text to insert.}

The inserted text can contain its own nested Pollen commands.

 
\ No newline at end of file diff --git a/doc/Top.html b/doc/Top.html index dc15c9c..6728a1f 100644 --- a/doc/Top.html +++ b/doc/Top.html @@ -1,2 +1,2 @@ -10.8 Top
6.1.0.5

10.8 Top

 (require pollen/top) package: pollen

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

syntax

(#%top . id)

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

Examples:

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

em: undefined;

 cannot reference undefined identifier

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

em: undefined;

 cannot reference undefined identifier

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

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

Examples:

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

'(em "Bonjour")

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

'(em "Bonjour")

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

Examples:

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

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

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

Examples:

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

'(erm "Bonjour")

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

Examples:

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

'(very-important-function "Bonjour")

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

syntax

(def/c id)

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

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

Examples:

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

very-important-function: undefined;

 cannot reference undefined identifier

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

Examples:

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

'(very-important-function "Bonjour")

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

Examples:

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

very-important-function: undefined;

 cannot reference undefined identifier

 
\ No newline at end of file +11.8 Top
6.1.0.5

11.8 Top

 (require pollen/top) package: pollen

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

syntax

(#%top . id)

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

Examples:

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

em: undefined;

 cannot reference undefined identifier

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

em: undefined;

 cannot reference undefined identifier

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

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

Examples:

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

'(em "Bonjour")

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

'(em "Bonjour")

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

Examples:

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

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

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

Examples:

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

'(erm "Bonjour")

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

Examples:

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

'(very-important-function "Bonjour")

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

syntax

(def/c id)

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

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

Examples:

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

very-important-function: undefined;

 cannot reference undefined identifier

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

Examples:

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

'(very-important-function "Bonjour")

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

Examples:

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

very-important-function: undefined;

 cannot reference undefined identifier

 
\ No newline at end of file diff --git a/doc/World.html b/doc/World.html index a499db1..dec8b19 100644 --- a/doc/World.html +++ b/doc/World.html @@ -1,5 +1,5 @@ -10.9 World
6.1.0.5

10.9 World

 (require pollen/world) package: pollen

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

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

A parameter that sets the HTTP port for the project server. Initialized to world:default-port, which defaults to 8080.

The two exports from a compiled Pollen source file. Initialized to 'doc and 'metas, respectively.

File implicitly required into every Pollen source file from its directory. Initialized to "project-require.rkt".

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

Name of directory where server support files live. Initialized to "server-extras".

A parameter that reports the path to the directory of support files for the project server. Initialized to #f, but set to a proper value when the server runs.

File extensions for Pollen source files, initialized to the following values:

world:preproc-source-ext = pp +11.9 World

6.1.0.5

11.9 World

 (require pollen/world) package: pollen

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

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

A parameter that sets the HTTP port for the project server. Initialized to world:default-port, which defaults to 8080.

The two exports from a compiled Pollen source file. Initialized to 'doc and 'metas, respectively.

File implicitly required into every Pollen source file from its directory. Initialized to "directory-require.rkt".

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

Name of directory where server support files live. Initialized to "server-extras".

A parameter that reports the path to the directory of support files for the project server. Initialized to #f, but set to a proper value when the server runs.

File extensions for Pollen source files, initialized to the following values:

world:preproc-source-ext = pp
world:markup-source-ext = pm
world:markdown-source-ext = pmd
world:null-source-ext = p @@ -9,4 +9,4 @@
world:mode-preproc = pre
world:mode-markup = markup
world:mode-markdown = markdown -
world:mode-pagetree = ptree

Pagetree that Pollen dashboard loads by default in each directory. Initialized to "index.ptree".

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

The magic character that indicates a Pollen command, function, or variable. Initialized to #\◊.

Prefix of the default template. Initialized to "template".

Used to generate the name of the fallback template (i.e., the template used to render a Pollen markup file when no other template can be found). Prefix is combined with the output suffix of the source file. Initialized to "fallback".

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

Default separators used in decoding. The first two are initialized to "\n"; the third to "\n\n".

CSS file used for the dashboard. Initialized to "poldash.css".

Paths not shown in the Pollen dashboard.

 
\ No newline at end of file +
world:mode-pagetree = ptree

Pagetree that Pollen dashboard loads by default in each directory. Initialized to "index.ptree".

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

The magic character that indicates a Pollen command, function, or variable. Initialized to #\◊.

Prefix of the default template. Initialized to "template".

Used to generate the name of the fallback template (i.e., the template used to render a Pollen markup file when no other template can be found). Prefix is combined with the output suffix of the source file. Initialized to "fallback".

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

Default separators used in decoding. The first two are initialized to "\n"; the third to "\n\n".

CSS file used for the dashboard. Initialized to "poldash.css".

Paths not shown in the Pollen dashboard.

 
\ No newline at end of file diff --git a/doc/big-picture.html b/doc/big-picture.html index a785fc1..0260957 100644 --- a/doc/big-picture.html +++ b/doc/big-picture.html @@ -1,3 +1,3 @@ -4 The big picture
6.1.0.5

4 The big picture

A summary of the key components & concepts of the Pollen publishing system and how they fit together. If you’ve completed the Quick tour, this will lend some context to what you saw. The next tutorials will make more sense if you read this first.

4.1 The book is a program

This is the core design principle of Pollen. Consistent with this principle, Pollen adopts the habits of software development in its functionality, workflow, and project management.

  • You are a programmer. Don’t panic. But let’s just admit it — if your book is a program, then you are, in part, programming it. You don’t have to know any programming to start using Pollen. But you’ll have to be willing to learn a few programming ideas. (Those who have programmed other template-based HTML generators may have to forget a few things.)

  • A Pollen project consists of source files + static files. A source file is a file that can be compiled to produce certain output. A 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.

  • 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 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.

4.2 One language, multiple dialects

  • Everything is Racket. The Pollen system is built entirely in the Racket programming language. Some of your source files will be in Racket. Others will be in one of the Pollen language dialects. But under the hood, everything becomes Racket code. So if you plan to do any serious work in Pollen, you’ll want to learn some basics about Racket too (for instance Quick: An Introduction to Racket with Pictures).

  • The Pollen language is based on Scribble. Scribble is a variant of the Racket language that flips the usual programming syntax: instead of code with embedded textual content, a Scribble source file is text with embedded code (an idea borrowed from TeX). The Pollen language is adapted from Scribble. So most things that are true about Scribble are also true about Pollen (see Scribble: The Racket Documentation Tool).

  • The Pollen language is divided into dialects. The Pollen dialects share a common syntax and structure. But they’re different in details that makes them better adapted to certain types of source files (for instance, one dialect of Pollen understands Markdown; the others don’t). Use whichever suits the task at hand.

4.3 Development environment

The Pollen development environment has three main pieces: the DrRacket code editor, the project server, and the command-line tool.

  • Edit source files with DrRacket. DrRacket is Racket’s GUI code editor. Sure, you can also use a generic text editor. But DrRacket lets you immediately run your source and see if it works.

  • Preview & test web pages with the Pollen project server. Pollen has a built-in development web server called the project server. After you start the project server, you can preview your web pages within any web browser, allowing you to test them with maximum accuracy.

  • Write the docs. The project server can recognize and render Scribble files, so you can use it as a previewing tool while you’re writing your documentation.

  • Render & deploy from the command line. Your Pollen project ultimately gets rendered to a set of static files (usually HTML and related assets). This can be controlled from the command line, so you can integrate it into other scripts.

4.4 A special data structure for HTML

Unlike other programming languages, Pollen (and Racket) internally represent HTML with something called an X-expression. An X-expression is simply a list that represents what in HTML is called an element, meaning a thing with an opening tag, a closing tag, and content in between. Like HTML elements, X-expressions can be nested. Unlike HTML elements, X-expressions have no closing tag, they use parentheses to denote the start and end, and text elements are put inside quotes.

For example, consider this HTML element:

<body><h1>Hello world</h1><p>Nice to <i>see</i> you.</p></body>

As a Racket X-expression, this would be written:

(body (h1 "Hello world") (p "Nice to " (i "see") " you."))

More will be said about X-expressions. But a couple advantages should be evident already. First, without the redundant angle brackets, the X-expression is more readable than the equivalent HTML. Second, an X-expression is preferable to representing HTML as a simple string, because it preserves the internal structure of the element.

4.5 Pollen command syntax

As mentioned above, a Pollen source file is not code with text embedded in it, but rather text with code embedded. (See ◊ command overview for more.)

  • If you can write text, you can program in Pollen. Really. As you already found out in the Quick tour, this is a valid Pollen program: +4 The big picture
    6.1.0.5

    4 The big picture

    A summary of the key components & concepts of the Pollen publishing system and how they fit together. If you’ve completed the Quick tour, this will lend some context to what you saw. The next tutorials will make more sense if you read this first.

    4.1 The book is a program

    This is the core design principle of Pollen. Consistent with this principle, Pollen adopts the habits of software development in its functionality, workflow, and project management.

    • You are a programmer. Don’t panic. But let’s just admit it — if your book is a program, then you are, in part, programming it. You don’t have to know any programming to start using Pollen. But you’ll have to be willing to learn a few programming ideas. (Those who have programmed other template-based HTML generators may have to forget a few things.)

    • A Pollen project consists of source files + static files. A source file is a file that can be compiled to produce certain output. A 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.

    • 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 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.

    4.2 One language, multiple dialects

    • Everything is Racket. The Pollen system is built entirely in the Racket programming language. Some of your source files will be in Racket. Others will be in one of the Pollen language dialects. But under the hood, everything becomes Racket code. So if you plan to do any serious work in Pollen, you’ll want to learn some basics about Racket too (for instance Quick: An Introduction to Racket with Pictures).

    • The Pollen language is based on Scribble. Scribble is a variant of the Racket language that flips the usual programming syntax: instead of code with embedded textual content, a Scribble source file is text with embedded code (an idea borrowed from TeX). The Pollen language is adapted from Scribble. So most things that are true about Scribble are also true about Pollen (see Scribble: The Racket Documentation Tool).

    • The Pollen language is divided into dialects. The Pollen dialects share a common syntax and structure. But they’re different in details that makes them better adapted to certain types of source files (for instance, one dialect of Pollen understands Markdown; the others don’t). Use whichever suits the task at hand.

    4.3 Development environment

    The Pollen development environment has three main pieces: the DrRacket code editor, the project server, and the command-line tool.

    • Edit source files with DrRacket. DrRacket is Racket’s GUI code editor. Sure, you can also use a generic text editor. But DrRacket lets you immediately run your source and see if it works.

    • Preview & test web pages with the Pollen project server. Pollen has a built-in development web server called the project server. After you start the project server, you can preview your web pages within any web browser, allowing you to test them with maximum accuracy.

    • Write the docs. The project server can recognize and render Scribble files, so you can use it as a previewing tool while you’re writing your documentation.

    • Render & deploy from the command line. Your Pollen project ultimately gets rendered to a set of static files (usually HTML and related assets). This can be controlled from the command line, so you can integrate it into other scripts.

    4.4 A special data structure for HTML

    Unlike other programming languages, Pollen (and Racket) internally represent HTML with something called an X-expression. An X-expression is simply a list that represents what in HTML is called an element, meaning a thing with an opening tag, a closing tag, and content in between. Like HTML elements, X-expressions can be nested. Unlike HTML elements, X-expressions have no closing tag, they use parentheses to denote the start and end, and text elements are put inside quotes.

    For example, consider this HTML element:

    <body><h1>Hello world</h1><p>Nice to <i>see</i> you.</p></body>

    As a Racket X-expression, this would be written:

    (body (h1 "Hello world") (p "Nice to " (i "see") " you."))

    More will be said about X-expressions. But a couple advantages should be evident already. First, without the redundant angle brackets, the X-expression is more readable than the equivalent HTML. Second, an X-expression is preferable to representing HTML as a simple string, because it preserves the internal structure of the element.

    4.5 Pollen command syntax

    As mentioned above, a Pollen source file is not code with text embedded in it, but rather text with code embedded. (See ◊ command overview for more.)

    • If you can write text, you can program in Pollen. Really. As you already found out in the Quick tour, this is a valid Pollen program:
      #lang pollen
      Hello world: how are you on this fine summer day?

    • Commands start with ◊. A simple rule: if a piece of text starts with , it’s treated as a command; otherwise it’s treated as ordinary text.

    • Write commands in text mode or Racket mode. Commands can use two equivalent notation systems: either Pollen’s text-mode command syntax, or standard Racket syntax.

    • Everything in Racket is in Pollen too. This isn’t some dimwit “template language.” Racket is a fully provisioned programming language, and every Racket function is available in Pollen.

    4.6 The preprocessor

    The preprocessor is the simplest processing mode in Pollen.

    • Text output. The preprocessor scans the source for any Pollen commands, resolves them, and outputs the whole file as text.

    • Work with any text file. I hope this blows your mind a teeny bit. You can use the preprocessor with HTML, CSS, Markdown, JavaScript, XML, SVG, or any other text-based file (including source files of other programming languages).

    • Start quickly. Because it works with any text file, the preprocessor is an easy way to try out Pollen, because you can mix it into your workflow on an existing project, or even just one file.

    4.7 Templated source files

    If you want to apply a particular page format to multiple sources of content — as you would in a book — you can use Pollen templates.

    • Templates can be any format. Usually Pollen templates will be HTML. But they don’t have to be.

    • Markdown support. Pollen has a built-in Markdown parser, so you can import Markdown sources into a Pollen publication.

    • Custom markup. Pollen’s markup mode allows you the freedom to define your own markup tags and attach behavior to them.

    • Mix source types. Every text source is converted to an X-expression before going into a template. So it’s fine to have multiple kinds of text source in one project.

    4.8 Pagetrees

    Similar to a table of contents, a pagetree is a special Pollen source file that gets turned into a hierarchical list of pages.

    • Navigation. Pagetrees are used to provide navigation links within HTML templates (like previous, next, up, top).

    • Organization. Multiple pagetrees can be used to divide your project into subsets of pages that should be treated separately.

     
    \ No newline at end of file diff --git a/doc/doc-index.html b/doc/doc-index.html index 6d53028..5f5749b 100644 --- a/doc/doc-index.html +++ b/doc/doc-index.html @@ -1,2 +1,2 @@ -Index

    Index

    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

     

    “Now you have two problems”
    #%top
    ->html
    ->markup-source-path
    ->null-source-path
    ->output-path
    ->pagenode
    ->preproc-source-path
    ->scribble-source-path
    ->template-source-path
    A special data structure for HTML
    Acknowledgments
    Adding commands
    Adding navigation links to the template with here
    Any command is valid
    Authoring mode
    Backstory
    Block
    block-txexpr?
    Cache
    cache-ref
    cached-require
    children
    Command syntax using ◊
    Creating a source file
    Creating valid HTML output
    current-cache
    current-pagetree
    Custom exports
    Decode
    decode
    def/c
    Defining variables with commands
    detect-linebreaks
    detect-paragraphs
    Development environment
    Enter Racket
    File
    File formats
    First tutorial
    First tutorial complete
    Further reading
    get-template-for
    Handling navigation boundaries with conditionals
    has-markup-source?
    has-null-source?
    has-preproc-source?
    has-scribble-source?
    has-template-source?
    has/is-markup-source?
    has/is-null-source?
    has/is-preproc-source?
    has/is-scribble-source?
    has/is-template-source?
    in-pagetree?
    index.ptree & the project server
    Inserting a comment
    Inserting specific source data into templates
    Inserting the value of a variable
    Inserting values from variables
    Inserting variables within CSS
    Installation
    Intermission
    Intermission
    Invoking other functions
    Invoking tag functions
    License & source code
    Linking to an external CSS file
    make-cache
    make-tag-function
    Making a custom template
    Making a pagetree file
    Making sure raco pollen works
    Markdown (.pmd extension)
    Markdown authoring mode
    Markdown in Pollen: two options
    Markdown mode
    Markup (.pm extension)
    Markup mode
    markup-source?
    Module reference
    Naming, saving, and rendering a source file
    Navigation
    next
    next*
    Null (.p extension)
    null-source?
    One language, multiple dialects
    pagenode?
    pagenodeish?
    Pagetree
    Pagetree (.ptree extension)
    Pagetree navigation
    pagetree->list
    pagetree-source?
    pagetree?
    Pagetrees
    Pagetrees
    parent
    path->pagenode
    pollen
    Pollen as a preprocessor
    Pollen command syntax
    pollen/cache
    pollen/decode
    pollen/file
    pollen/markdown
    pollen/markup
    pollen/pagetree
    pollen/pre
    pollen/ptree
    pollen/render
    pollen/tag
    pollen/template
    pollen/top
    pollen/world
    Pollen: the book is a program
    Prelude: my principled objection to Markdown
    preproc-source?
    Preprocessor (.pp extension)
    Prerequisites
    Prerequisites
    previous
    previous*
    project-block-tags
    PS for Scribble users
    Putting in the text of the poem
    Quick tour
    Racket basics (if you’re not familiar)
    raco pollen
    raco pollen clone
    raco pollen help
    raco pollen render
    raco pollen start
    register-block-tag
    Render
    render
    render-batch
    render-pagetree
    render-to-file
    render-to-file-if-needed
    reset-cache
    Rethinking the solution for digital books
    Running a source file
    Saving & naming your source file
    Scribble (.scrbl extension)
    scribble-source?
    Second tutorial
    Second tutorial complete
    select
    select*
    select-from-doc
    select-from-metas
    Setting the #lang line
    Setting up a preprocessor source file
    siblings
    smart-dashes
    smart-quotes
    Source files in the dashboard
    Source formats
    Standard exports
    Starting a new file in DrRacket
    Starting the project server with raco pollen
    Tag
    Template
    template-source?
    Templated source files
    Templates
    Templates
    The ->html function and the doc variable
    The better idea: a programming model
    The big picture
    The book is a program
    The command name
    The end of the beginning
    The golden rule
    The lozenge glyph (◊)
    The preprocessor
    The project server
    The project-require.rkt file
    The Racket arguments
    The relationship of Racket & Pollen
    The text argument
    The two command modes: text mode & Racket mode
    Top
    Typography
    Using Markdown with the preprocessor
    Using raco pollen
    Using the automatic pagetree
    Using the dashboard
    Using the project server
    Utilities
    Utility formats
    validate-pagetree
    Web development and its discontents
    What is Pollen?
    when/block
    whitespace/nbsp?
    whitespace?
    Working with the preprocessor
    World
    world:check-project-requires-in-render?
    world:command-marker
    world:current-server-extras-path
    world:current-server-port
    world:dashboard-css
    world:decodable-extensions
    world:default-pagetree
    world:default-port
    world:default-template-prefix
    world:fallback-template-prefix
    world:linebreak-separator
    world:main-pollen-export
    world:markdown-source-ext
    world:markup-source-ext
    world:meta-pollen-export
    world:mode-auto
    world:mode-markdown
    world:mode-markup
    world:mode-pagetree
    world:mode-preproc
    world:newline
    world:null-source-ext
    world:pagetree-root-node
    world:pagetree-source-ext
    world:paragraph-separator
    world:paths-excluded-from-dashboard
    world:preproc-source-ext
    world:project-require
    world:scribble-source-ext
    world:server-extras-dir
    world:template-meta-key
    world:template-source-ext
    wrap-hanging-quotes
    X-expressions
    ◊ command overview

     
    \ No newline at end of file +Index

    Index

    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

     

    “But I really need XML…”
    “Now you have two problems”
    #%top
    ->html
    ->markup-source-path
    ->null-source-path
    ->output-path
    ->pagenode
    ->preproc-source-path
    ->scribble-source-path
    ->template-source-path
    A special data structure for HTML
    Acknowledgments
    Adding commands
    Adding navigation links to the template with here
    Any command is valid
    Attaching behavior to tags
    Attributes
    Authoring mode
    Backstory
    Block
    block-txexpr?
    Cache
    cache-ref
    cached-require
    children
    Choosing custom tags
    Command syntax using ◊
    Creating a Pollen markup file
    Creating a source file
    Creating valid HTML output
    current-cache
    current-pagetree
    Custom exports
    Decode
    decode
    def/c
    Defining variables with commands
    detect-linebreaks
    detect-paragraphs
    Development environment
    Enter Racket
    File
    File formats
    First tutorial
    First tutorial complete
    Format independence
    Further reading
    get-template-for
    Handling navigation boundaries with conditionals
    has-markup-source?
    has-null-source?
    has-preproc-source?
    has-scribble-source?
    has-template-source?
    has/is-markup-source?
    has/is-null-source?
    has/is-preproc-source?
    has/is-scribble-source?
    has/is-template-source?
    in-pagetree?
    index.ptree & the project server
    Inserting a comment
    Inserting specific source data into templates
    Inserting the value of a variable
    Inserting values from variables
    Inserting variables within CSS
    Installation
    Intermission
    Intermission
    Intermission
    Interpolating variables into strings
    Invoking other functions
    Invoking tag functions
    License & source code
    Linking to an external CSS file
    make-cache
    make-default-tag-function
    Making a custom template
    Making a pagetree file
    Making sure raco pollen works
    Markdown (.pmd extension)
    Markdown authoring mode
    Markdown in Pollen: two options
    Markdown mode
    Markup (.pm extension)
    Markup mode
    markup-source?
    Module reference
    Multiple input values & rest arguments
    Naming, saving, and rendering a source file
    Navigation
    next
    next*
    Notes for experienced programmers
    Null (.p extension)
    null-source?
    One language, multiple dialects
    Organizing functions
    pagenode?
    pagenodeish?
    Pagetree
    Pagetree (.ptree extension)
    Pagetree navigation
    pagetree->list
    pagetree-source?
    pagetree?
    Pagetrees
    Pagetrees
    parent
    Parsing attributes
    path->pagenode
    Point of no return
    pollen
    Pollen as a preprocessor
    Pollen command syntax
    Pollen markup vs. XML
    pollen/cache
    pollen/decode
    pollen/file
    pollen/markdown
    pollen/markup
    pollen/pagetree
    pollen/pre
    pollen/ptree
    pollen/render
    pollen/tag
    pollen/template
    pollen/top
    pollen/world
    Pollen: the book is a program
    Prelude: my principled objection to Markdown
    preproc-source?
    Preprocessor (.pp extension)
    Prerequisites
    Prerequisites
    Prerequisites
    previous
    previous*
    project-block-tags
    PS for Scribble users
    Putting in the text of the poem
    Putting it all together
    Quick tour
    Racket basics (if you’re not familiar)
    raco pollen
    raco pollen clone
    raco pollen help
    raco pollen render
    raco pollen start
    register-block-tag
    Render
    render
    render-batch
    render-pagetree
    render-to-file
    render-to-file-if-needed
    reset-cache
    Rethinking the solution for digital books
    Returning an X-expression
    Running a source file
    Saving & naming your source file
    Scribble (.scrbl extension)
    scribble-source?
    Second tutorial
    Second tutorial complete
    select
    select*
    select-from-doc
    select-from-metas
    Semantic markup
    Setting the #lang line
    Setting up a preprocessor source file
    siblings
    smart-dashes
    smart-quotes
    Source files in the dashboard
    Source formats
    split-attributes
    Standard exports
    Starting a new file in DrRacket
    Starting the project server with raco pollen
    Tag
    Tags & tag functions
    Tags are functions
    Template
    template-source?
    Templated source files
    Templates
    Templates
    The ->html function and the doc variable
    The better idea: a programming model
    The big picture
    The book is a program
    The command name
    The directory-require.rkt file
    The directory-require.rkt file
    The end of the beginning
    The golden rule
    The lozenge glyph (◊)
    The preprocessor
    The project server
    The Racket arguments
    The relationship of Racket & Pollen
    The text argument
    The two command modes: text mode & Racket mode
    The XML problem
    Third tutorial
    Top
    Typography
    Using custom tags
    Using Markdown with the preprocessor
    Using Racket’s function libraries
    Using raco pollen
    Using the automatic pagetree
    Using the dashboard
    Using the project server
    Utilities
    Utility formats
    validate-pagetree
    Web development and its discontents
    What are custom tags good for?
    What is Pollen?
    What Pollen markup does differently
    when/block
    whitespace/nbsp?
    whitespace?
    Working with the preprocessor
    World
    world:check-directory-requires-in-render?
    world:command-marker
    world:current-server-extras-path
    world:current-server-port
    world:dashboard-css
    world:decodable-extensions
    world:default-pagetree
    world:default-port
    world:default-template-prefix
    world:directory-require
    world:fallback-template-prefix
    world:linebreak-separator
    world:main-pollen-export
    world:markdown-source-ext
    world:markup-source-ext
    world:meta-pollen-export
    world:mode-auto
    world:mode-markdown
    world:mode-markup
    world:mode-pagetree
    world:mode-preproc
    world:newline
    world:null-source-ext
    world:pagetree-root-node
    world:pagetree-source-ext
    world:paragraph-separator
    world:paths-excluded-from-dashboard
    world:preproc-source-ext
    world:scribble-source-ext
    world:server-extras-dir
    world:template-meta-key
    world:template-source-ext
    wrap-hanging-quotes
    Writing with Pollen markup
    X-expressions
    ◊ command overview

     
    \ No newline at end of file diff --git a/doc/file-types.html b/doc/file-types.html index cc51fa1..8c358a9 100644 --- a/doc/file-types.html +++ b/doc/file-types.html @@ -1,2 +1,2 @@ -10.3 File
    6.1.0.5

    10.3 File

     (require pollen/file) package: pollen

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

    Pollen handles six kinds of source files:

    Preprocessor, with file extension .pp.

    Markup, with file extension .pm.

    Template, with file extension .pt.

    Null, with file extension .p.

    Scribble, with file extension .scrbl.

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

    procedure

    (preproc-source? v)  boolean?

      v : any/c

    procedure

    (markup-source? v)  boolean?

      v : any/c

    procedure

    (template-source? v)  boolean?

      v : any/c

    procedure

    (null-source? v)  boolean?

      v : any/c

    procedure

    (scribble-source? v)  boolean?

      v : any/c

    procedure

    (pagetree-source? v)  boolean?

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

    Examples:

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

    #t

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

    #t

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

    #t

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

    #t

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

    #t

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

    #t

    procedure

    (has-preproc-source? v)  boolean?

      v : any/c

    procedure

    (has-markup-source? v)  boolean?

      v : any/c

    procedure

    (has-template-source? v)  boolean?

      v : any/c

    procedure

    (has-null-source? v)  boolean?

      v : any/c

    procedure

    (has-scribble-source? v)  boolean?

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

    procedure

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

      v : any/c

    procedure

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

      v : any/c

    procedure

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

      v : any/c

    procedure

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

      v : any/c

    procedure

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

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

    procedure

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

      p : pathish?

    procedure

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

      p : pathish?

    procedure

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

      p : pathish?

    procedure

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

      p : pathish?

    procedure

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

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

    Examples:

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

    #<path:default.html.pp>

    > (->markup-source-path name)

    #<path:default.html.pm>

    > (->template-source-path name)

    #<path:default.html.pt>

    > (->scribble-source-path name)

    #<path:default.scrbl>

    > (->null-source-path name)

    #<path:default.html.p>

    procedure

    (->output-path p)  path?

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

    Examples:

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

    #<path:main.css>

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

    #<path:default.html>

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

    #<path:index.html>

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

    #<path:file.html>

     
    \ No newline at end of file +11.3 File
    6.1.0.5

    11.3 File

     (require pollen/file) package: pollen

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

    Pollen handles six kinds of source files:

    Preprocessor, with file extension .pp.

    Markup, with file extension .pm.

    Template, with file extension .pt.

    Null, with file extension .p.

    Scribble, with file extension .scrbl.

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

    procedure

    (preproc-source? v)  boolean?

      v : any/c

    procedure

    (markup-source? v)  boolean?

      v : any/c

    procedure

    (template-source? v)  boolean?

      v : any/c

    procedure

    (null-source? v)  boolean?

      v : any/c

    procedure

    (scribble-source? v)  boolean?

      v : any/c

    procedure

    (pagetree-source? v)  boolean?

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

    Examples:

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

    #t

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

    #t

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

    #t

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

    #t

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

    #t

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

    #t

    procedure

    (has-preproc-source? v)  boolean?

      v : any/c

    procedure

    (has-markup-source? v)  boolean?

      v : any/c

    procedure

    (has-template-source? v)  boolean?

      v : any/c

    procedure

    (has-null-source? v)  boolean?

      v : any/c

    procedure

    (has-scribble-source? v)  boolean?

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

    procedure

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

      v : any/c

    procedure

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

      v : any/c

    procedure

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

      v : any/c

    procedure

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

      v : any/c

    procedure

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

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

    procedure

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

      p : pathish?

    procedure

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

      p : pathish?

    procedure

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

      p : pathish?

    procedure

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

      p : pathish?

    procedure

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

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

    Examples:

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

    #<path:default.html.pp>

    > (->markup-source-path name)

    #<path:default.html.pm>

    > (->template-source-path name)

    #<path:default.html.pt>

    > (->scribble-source-path name)

    #<path:default.scrbl>

    > (->null-source-path name)

    #<path:default.html.p>

    procedure

    (->output-path p)  path?

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

    Examples:

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

    #<path:main.css>

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

    #<path:default.html>

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

    #<path:index.html>

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

    #<path:file.html>

     
    \ No newline at end of file diff --git a/doc/first-tutorial.html b/doc/first-tutorial.html index eaa8166..e6998e6 100644 --- a/doc/first-tutorial.html +++ b/doc/first-tutorial.html @@ -1,13 +1,6 @@ -5 First tutorial
    6.1.0.5

    5 First tutorial

    In this tutorial, you’ll use Pollen to make a single HTML page with a poem. You’ll learn about:

    • The relationship of Racket & Pollen

    • DrRacket

    • The project server

    • The preprocessor

    If you want the shortest possible introduction to Pollen, try the Quick tour.

    5.1 Prerequisites

    I’m going to assume that you’ve already installed Racket and Pollen. If not, do that now.

    I’m also going to assume you know the basics of using a command line to run programs and navigate the file system using commands like cd and ls. On Mac OS X, your command-line program is called Terminal; on Windows it’s the Windows Command Processor.

    5.2 The relationship of Racket & Pollen

    As I mentioned in the The big picture, Pollen is built using Racket, and everything in Pollen ultimately becomes Racket code. If you’re comfortable with that idea, you may move along.

    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 other programming languages. These languages might look & behave like Racket. Or they 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 domain-specific languages, or DSLs.

    Racket exploits the fact that under the hood, all programming languages are basically doing the same thing. (CS jocks know this more formally as a side effect of Turing completeness.) Racket starts with the most general expression of a Turing-complete language — called the lambda calculus — and lets users build on that. In most programming languages, you can build functions, classes, and modules. But in Racket, you can alter anything about the language.

    If you find this a strange idea, you’re not alone. Most programmers — and until recently, me too — have never made or used DSLs. If you have a programming problem to solve, you start with a general-purpose language like Python or Java or Ruby, and go from there. Nothing wrong with that.

    But programming languages contain their own design choices and compromises. Sometimes the problem at hand is best solved by manipulating the language at a deeper level. When you make a DSL, you’re still programming in the underlying language, but doing so at a point of higher leverage.

    Pollen is a DSL implemented in Racket. It is a close cousin of Scribble: The Racket Documentation Tool, another Racket DSL, which was designed for writing Racket documentation. The key feature of Scribble, and thus also of Pollen, is that it’s text-based. Meaning, whereas most languages have source files made of code with text embedded within, Pollen’s source files are text with code embedded within.

    Moreover, Pollen is meant to be a small step away from Racket — you can think of it as a more convenient notation system for Racket code, similar to how Markdown is a more convenient notation for HTML. But unlike Markdown, which only lets you access a subset of HTML, anything that can be done in Racket can also be done in Pollen.

    As you work more with Pollen, you’ll pick up more about how Pollen corresponds to Racket (see ◊ command overview) and easily be able to convert commands from one system to the other. In later tutorials, you’ll see how larger Pollen projects are made out of both Pollen and Racket source files.

    But in smaller projects, like this one, you can just use Pollen.

    5.3 Starting a new file in DrRacket

    DrRacket is the IDE for the Racket programming language, and other languages made with Racket (like Pollen). IDE stands for “Integrated Development Environment,” which is a fancy phrase for “a nice place to edit and run your code.” DrRacket is installed as part of the core Racket distribution.

    If you’ve worked with languages like Perl, Python, or Ruby, you may be more familiar with using a general-purpose text editor to edit your code, and then running your program at the command line. You can do that with Racket too. But DrRacket is a considerately designed tool. I recommend it. For these tutorials, I’ll assume you’re using DrRacket. If you insist on using the command line, I trust you to figure out what you need to do to keep up.

    Launch DrRacket. Start a new file. The code in the file will look like this:

    #lang racket

    Within the main window, you should also see an interactions window, which shows the output of the current file, and starts out looking something like this (details, like the version number, will vary):

    Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m].

    Language: racket; memory limit: 1000 MB.

    >

    If you don’t see the interactions window, select View|Show Interactions from the menu.

    5.3.1 Setting the #lang line

    The first line of every Racket source file, and every Pollen source file, is called the #lang line. The #lang line identifies the language used to interpret the rest of the file.

    For more about the #lang line, see The #lang Shorthand.

    When you start a new Pollen source file in DrRacket, you’ll need to change the #lang line to the Pollen language. The simplest way is to change the first line to this:

    #lang pollen

    Now run your file by clicking the Run button in the upper-right corner, or select Racket|Run from the menu. You’ll get something like:

    Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m].

    Language: pollen; memory limit: 1000 MB.

    >

    Notice that the language is now reported as pollen. If you like, change the #lang line to this:

    #lang pollenxyz

    Then click Run again. DrRacket will print an error:

    Module Language: invalid module text -
    standard-module-name-resolver: collection not found ...

    Why? Because there’s no language called pollenxyz. Switch it back to pollen and let’s move on.

    5.3.2 Putting in the text of the poem

    Here’s a short, bad poem I wrote about CSS.

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    Paste the text of this poem into your DrRacket editing window, below the #lang line, so it looks like this:

    #lang pollen

     

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    Run the file again. In the interactions window, you’ll see:

    The margin is 8em. -
    The border is blue. -
    The padding is 2em. -
    The border is too.

    This shows you something important: by default, any plain text in a Pollen source file is simply printed as written when you Run the file (minus the #lang line, which is just for Racket’s benefit). If you like, edit the text of the poem and click Run again. You’ll see the updated text printed in the interactions window.

    5.3.3 Saving & naming your source file

    File naming in Pollen is consequential.

    Ultimately, every Pollen source file in your project will be rendered into an output file. Each Pollen source file corresponds to one output file. The name of this output file will be the name of the source file minus the Pollen source extension. So a source file called file.txt.pp will become file.txt.

    Thus, to build the name of a source file, we take the name we want for the output file and add the appropriate Pollen file extension. Different Pollen source files use different extensions — but more about that later. For now, the extension you’ll use for your source is .pp.

    In this case, let’s say we want to end up with a file called poem.html. Therefore, the name of our source file needs to be:

    the output name poem.html + the source extension .pp = poem.html.pp

    (If you want to name the file something-else.html.pp, be my guest. There’s no magic associated with the prefix.)

    You’re welcome to change the name of your source files from the desktop. On Mac OS X and Windows, however, the desktop interface often hides file extensions, so check the properties of the file afterward to make sure you got the name you expected.

    In a convenient location (e.g., your home directory or the desktop) create a new directory for your project called tutorial. In this new directory, save your DrRacket file as poem.html.pp.

    "/path/to/tutorial/poem.html.pp"

    #lang pollen

     

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    5.4 Using the project server

    The project server is a web server built into Pollen. Just as DrRacket lets you run individual files and see if they work as you expect, the project server lets you preview and test your project as a real website. While working on your Pollen project, you may find it convenient to have DrRacket open on half your screen, and on the other half, a web browser pointing at the project server.

    “Why can’t I just open the HTML files directly in my browser?” If you want to keep making web pages the way we did in 1996, go ahead. But that approach has several shortcomings. First, when you open files directly in your browser, you’re cruising the local filesystem, and absolute URLs (the kind that start with a /) won’t work. Second, if you want to test your website on devices other than your own machine — well, you can’t. Third, you have to render your HTML files in advance, whereas the project server is clever about doing this dynamically.

    So use the project server.

    A note about security. The project server isn’t intended for real-world use, but rather as a development tool. That said, once you start the project server, it’s an actual web server running on your machine, and it will respond to requests from any computer. If you want to limit traffic to your local network, or certain machines on your local network, it’s your job — not mine — to configure your firewall or other network security measures accordingly.

    5.4.1 Starting the project server with raco pollen

    Before we start the project server, a word about the raco pollen command.

    When you installed Racket, Racket installed a utility program called raco. This name is short for Racket command, and raco acts as a hub for — you guessed it — Racket commands. You used it when you first installed Pollen:

    > raco pkg install pollen

    The first argument after raco is the subcommand. For instance, raco pkg ... lets you install, update, and remove packages like so:

    > raco pkg update pollen

    > raco pkg remove pollen

    Likewise, raco pollen lets you issue commands relevant to Pollen, like starting the project server. (See Using raco pollen for a full description of available commands.)

    Now we’ll start the project server. Go to your command line and enter the following:

    > cd /path/to/tutorial

    > raco pollen start

    Windows users, I’ll trust you to convert raco into the appropriate command for your system — assuming defaults, it’s likely to be "C:\Program Files\Racket\raco" (include the surrounding quotes in the command).

    After a moment, you’ll see a startup message like this:

    Welcome to Pollen 0.001 (Racket 6.x.x.x)

    Project root is /path/to/tutorial

    Project server is http://localhost:8080 (Ctrl-C to exit)

    Project dashboard is http://localhost:8080/index.ptree

    Ready to rock

    Project root means the directory that the project server was started in, and which it’s treating as its root directory. Any absolute URLs (i.e., those beginning with /) will resolve into this directory. So a URL like /styles.css will impliedly become /path/to/tutorial/styles.css.

    If you use the bare command raco pollen start, the project server will start in the current directory. But if you want to start the project server elsewhere, you can add that directory as an argument like this:

    > raco pollen start /some/other/path

    The next line of the startup message tells you that the web address of the project server is http://localhost:8080. This is the address you put into your web browser to test your project. If you’re unfamiliar with this style of URL, localhost refers to your own machine, and 8080 is the network port where the project server will respond to browser requests.

    If you want to access the project server from a different machine, you can’t use localhost. But you can use the IP address of the machine running the project server (e.g., http://192.168.1.10:8080) or any name for that machine available through local DNS (e.g., http://mb-laptop:8080).

    Though port 8080 is the default, you can start the project server on any port you like by adding it as an argument to raco pollen start:

    > raco pollen start /path/to/tutorial

    > raco pollen start /path/to/tutorial 8088

    You can also change the default port by altering world:default-port, or parameterizing it with world:current-server-port.

    Note that when you pass a port argument, you also have to pass a path argument. If you want the project server to start in the current directory, you can use the usual . shorthand:

    > cd /path/to/tutorial

    > raco pollen start 8088

    /path/to/tutorial/8088 is not a directory

    > raco pollen start . 8088

    You can run multiple project servers simultaneously. Just start them on different ports so they don’t conflict with each other.

    Your terminal window will report status and error messages from the project server as it runs. Use Ctrl-C to stop the server.

    5.4.2 Using the dashboard

    For each directory in your project, starting at the top, the project server displays a dashboard in your web browser. The dashboard gives you an overview of the files in the directory, and links to view them.

    The address of the top-level dashboard is http://localhost:8080/index.ptree. Other dashboards follow the same pattern (e.g., http://localhost:8080/path/to/dir/index.ptree.)

    Note that the dashboard is not at http://localhost:8080/ or its equivalent, http://localhost:8080/index.html. Why? So it doesn’t interfere with any index.html that you may want to put in your project.

    Thus, index.ptree. The .ptree extension is short for pagetree. In Pollen, a pagetree is a hierarchical list of pages. We’ll do more with pagetrees in a later tutorial. For now, just be aware that to generate the dashboard, the project server will first look for an actual index.ptree file in each directory. If it doesn’t find one, it will generate a pagetree from a listing of files in the directory.

    Let’s look at the root-level dashboard for our project. First, make sure your project server is running:

    > cd /path/to/tutorial

    > raco pollen start

    Then, in your web browser, visit http://localhost:8080/index.ptree.

    You should see something like this:

    The top line tells us that we’re in the root directory of the project. We didn’t make an explicit index.ptree file, so the project server just shows us a directory listing.

    5.4.3 Source files in the dashboard

    We see the only file, poem.html.pp. Note that the .pp extension is grayed out. The dashboard automatically consolidates references to source and output files into a single entry. What this entry says is “The directory contains a source file in .pp format for the output file poem.html.”

    Every source-file entry in the dashboard has three links. The first link is attached to the filename itself, and takes you to a preview of the output file. If the output file doesn’t yet exist — as is the case here — it will be dynamically rendered. (This is true whether you click its name in the dashboard, or link to it from another page.) So click the filename. You’ll see in your web browser:

    The margin is 42em. The border is red. The padding is 15em. The border is too.

    Granted, this is a boring web page. The main point here is that you’re seeing the output from your source file, which didn’t exist before. Notice that the address bar says http://localhost:8080/poem.html, not poem.html.pp. And if you look in your tutorial directory, you’ll see a new file called poem.html.

    In other words, when you clicked on the filename link in the dashboard, Pollen rendered the output file from your source file and saved it in your project directory. As promised earlier, the name of the output file (poem.html) is the name of the source file (poem.html.pp) minus the Pollen extension (.pp).

    If you go back to the dashboard and click on the filename link again, you’ll see the same output file. If the source file hasn’t changed, Pollen will just show you the output file that’s already been rendered.

    But if you like, open your poem.html.pp source file in DrRacket, edit the first two lines, and save the file:

    #lang pollen

     

    The cave is pitch black.

    Look out for the grue.

    The padding is 15em.

    The border is too.

    Go back to the dashboard and click on the filename. This time, you’ll see:

    The cave is pitch black. Look out for the grue. The padding is 15em. The border is too.

    Here, Pollen notices that the source file has changed, so it refreshes the output file. This makes it convenient to work between DrRacket and your web browser, editing source and then reloading to see the changes.

    The other two links in the dashboard are labeled in and out.

    The link labeled in will display the contents of the source file:

    #lang pollen

     

    The cave is pitch black.

    Look out for the grue.

    The padding is 15em.

    The border is too.

    The link labeled out will display the contents of the output file (just like the “view source” option in your web browser):

    The cave is pitch black.

    Look out for the grue.

    The padding is 15em.

    The border is too.

    For now, the files are identical except for the #lang line. But let’s change that.

    5.5 Working with the preprocessor

    Pollen can operate in several processing modes. One of these is preprocessor mode. A preprocessor is a tool for making systematic, automated changes to a file, often in contemplation of further processing (hence the pre-). You can use the Pollen preprocessor this way. Or you can just use it on its own, and leave your files in a finished state.

    That’s how we’ll use it in this tutorial. We’ll build out our poem.html.pp source file so that it exits the preprocessor as a legit HTML file.

    5.5.1 Setting up a preprocessor source file

    The file extension of a Pollen source file tells Pollen what kind of processing to apply to it. The “.pp” file extension stands for “Pollen preprocessor.” You can use the preprocessor with any text-based file by: -
    • inserting #lang pollen as the first line,

    • adding the .pp file extension,

    • running it through Pollen.

    For more about the Pollen processing modes and how to invoke them, see File.

    “The preprocessor be used with any kind of text-based file?” Right. “But how?” The preprocessor reads the source file, handles any Pollen commands it finds, and lets the rest of the content pass through untouched. To the preprocessor, it’s all just text data. It doesn’t care whether that text represents HTML, CSS, JavaScript, or even TI-BASIC.

    Because the preprocessor only deals in text, the Pollen commands you use in the preprocessor also have to produce text. Moreover, Pollen doesn’t enforce the semantics of the underlying file — that’s your responsibility. For instance, Pollen won’t stop you from doing nonsensical things like this:

    "bad-poem.html.pp"

    #lang pollen

     

    The cave is pitch black.

    Look out for the grue.

    ◊(insert-mp3-recording-of-scream)

    Here, the result is not going to be valid HTML, because you can’t simply drop binary data in the middle of an HTML file. To paraphrase Mr. Babbage — garbage in, garbage out.

    I’ve encouraged you to mess with the source file, but let’s return it to its original state:

    "/path/to/tutorial/poem.html.pp"

    #lang pollen

     

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    This file has #lang pollen as the first line, and .pp as the file extension, so it meets the minimum requirements for the preprocessor.

    5.5.2 Creating valid HTML output

    Let’s update our source so it produces valid HTML. Edit the source as follows:

    "/path/to/tutorial/poem.html.pp"

    #lang pollen

    <!DOCTYPE html>

    <html>

    <body>

    <pre>

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    </pre>

    </body>

    </html>

    Return to the project server and view http://localhost:8080/poem.html. Earlier, the output looked like this:

    The margin is 42em. The border is red. The padding is 15em. The border is too.

    But now, because of the <pre> tag, the poem will appear in a monospaced font, and the line breaks will be preserved:

    The margin is 42em. -
    The border is red. -
    The padding is 15em. -
    The border is too.

    As before, because the source has changed, Pollen refreshes the output file. From the dashboard, you can use the in and out links to inspect the source and output.

    This is now a valid HTML page.

    5.5.3 Adding commands

    I mentioned that the preprocessor reads the file and handles any Pollen commands it finds. But our source file doesn’t have any commands yet. Let’s add some.

    Pollen commands can be embedded in your source file using one of two modes: Racket mode or text mode. We’ll try text mode in a later tutorial. For now, we’ll use Racket mode.

    To make a Racket-mode Pollen command, just take any Racket expression and put the lozenge character () in front of it. For instance, these are valid Racket expressions:

    (define inner 2)

    (define edge (* inner 4))

    (define color "blue")

    And these are the equivalent commands in Pollen:

    ◊(define inner 2)

    ◊(define edge (* inner 4))

    ◊(define color "blue")

    How to type a lozenge: +5 First tutorial

    6.1.0.5

    5 First tutorial

    In this tutorial, you’ll use Pollen to make a single HTML page with a poem. You’ll learn about:

    • The relationship of Racket & Pollen

    • DrRacket

    • The project server

    • The preprocessor

    If you want the shortest possible introduction to Pollen, try the Quick tour.

    5.1 Prerequisites

    I’m going to assume that you’ve already installed Racket and Pollen. If not, do that now.

    I’m also going to assume you know the basics of using a command line to run programs and navigate the file system using commands like cd and ls. On Mac OS X, your command-line program is called Terminal; on Windows it’s the Windows Command Processor.

    5.2 The relationship of Racket & Pollen

    As I mentioned in the The big picture, Pollen is built using Racket, and everything in Pollen ultimately becomes Racket code. If you’re comfortable with that idea, you may move along.

    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 other programming languages. These languages might look & behave like Racket. Or they 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 domain-specific languages, or DSLs.

    Racket exploits the fact that under the hood, all programming languages are basically doing the same thing. (CS jocks know this more formally as a side effect of Turing completeness.) Racket starts with the most general expression of a Turing-complete language — called the lambda calculus — and lets users build on that. In most programming languages, you can build functions, classes, and modules. But in Racket, you can alter anything about the language.

    If you find this a strange idea, you’re not alone. Most programmers — and until recently, me too — have never made or used DSLs. If you have a programming problem to solve, you start with a general-purpose language like Python or Java or Ruby, and go from there. Nothing wrong with that.

    But programming languages contain their own design choices and compromises. Sometimes the problem at hand is best solved by manipulating the language at a deeper level. When you make a DSL, you’re still programming in the underlying language, but doing so at a point of higher leverage.

    Pollen is a DSL implemented in Racket. It is a close cousin of Scribble: The Racket Documentation Tool, another Racket DSL, which was designed for writing Racket documentation. The key feature of Scribble, and thus also of Pollen, is that it’s text-based. Meaning, whereas most languages have source files made of code with text embedded within, Pollen’s source files are text with code embedded within.

    Moreover, Pollen is meant to be a small step away from Racket — you can think of it as a more convenient notation system for Racket code, similar to how Markdown is a more convenient notation for HTML. But unlike Markdown, which only lets you access a subset of HTML, anything that can be done in Racket can also be done in Pollen.

    As you work more with Pollen, you’ll pick up more about how Pollen corresponds to Racket (see ◊ command overview) and easily be able to convert commands from one system to the other. In later tutorials, you’ll see how larger Pollen projects are made out of both Pollen and Racket source files.

    But in smaller projects, like this one, you can just use Pollen.

    5.3 Starting a new file in DrRacket

    DrRacket is the IDE for the Racket programming language, and other languages made with Racket (like Pollen). IDE stands for “Integrated Development Environment,” which is a fancy phrase for “a nice place to edit and run your code.” DrRacket is installed as part of the core Racket distribution.

    If you’ve worked with languages like Perl, Python, or Ruby, you may be more familiar with using a general-purpose text editor to edit your code, and then running your program at the command line. You can do that with Racket too. But DrRacket is a considerately designed tool. I recommend it. For these tutorials, I’ll assume you’re using DrRacket. If you insist on using the command line, I trust you to figure out what you need to do to keep up.

    Launch DrRacket. Start a new file. The code in the file will look like this:

    #lang racket

    Within the main window, you should also see an interactions window, which shows the output of the current file, and starts out looking something like this (details, like the version number, will vary):

    Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m].

    Language: racket; memory limit: 1000 MB.

    >

    If you don’t see the interactions window, select View|Show Interactions from the menu.

    5.3.1 Setting the #lang line

    The first line of every Racket source file, and every Pollen source file, is called the #lang line. The #lang line identifies the language used to interpret the rest of the file.

    For more about the #lang line, see Defining new #lang Languages. BTW, it’s pronounced hash lang.

    When you start a new Pollen source file in DrRacket, you’ll need to change the #lang line to the Pollen language. The simplest way is to change the first line to this:

    #lang pollen

    Now run your file by clicking the Run button in the upper-right corner, or select Racket|Run from the menu. You’ll get something like:

    Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m].

    Language: pollen; memory limit: 1000 MB.

    >

    Notice that the language is now reported as pollen. If you like, change the #lang line to this:

    #lang pollenxyz

    Then click Run again. DrRacket will print an error:

    Module Language: invalid module text
    standard-module-name-resolver: collection not found ...

    Why? Because there’s no language called pollenxyz. Switch it back to pollen and let’s move on.

    5.3.2 Putting in the text of the poem

    Here’s a short, bad poem I wrote about CSS.

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    Paste the text of this poem into your DrRacket editing window, below the #lang line, so it looks like this:

    #lang pollen
     
    The margin is 42em.
    The border is red.
    The padding is 15em.
    The border is too.

    Run the file again. In the interactions window, you’ll see:

    The margin is 8em.
    The border is blue.
    The padding is 2em.
    The border is too.

    This shows you something important: by default, any plain text in a Pollen source file is simply printed as written when you Run the file (minus the #lang line, which is just for Racket’s benefit). If you like, edit the text of the poem and click Run again. You’ll see the updated text printed in the interactions window.

    5.3.3 Saving & naming your source file

    Don’t skip this section! It explains a concept that’s essential to understanding how Pollen works.

    File naming in Pollen is consequential.

    Ultimately, every Pollen source file in your project will be rendered into an output file. Each Pollen source file corresponds to one output file. The name of this output file will be the name of the source file minus the Pollen source extension. So a source file called file.txt.pp will become file.txt.

    Thus, to build the name of a source file, we take the name we want for the output file and add the appropriate Pollen file extension. Different Pollen source files use different extensions — but more about that later. For now, the extension you’ll use for your source is .pp.

    In this case, let’s say we want to end up with a file called poem.html. Therefore, the name of our source file needs to be:

    the output name poem.html + the source extension .pp = poem.html.pp

    (If you want to name the file something-else.html.pp, be my guest. There’s no magic associated with the prefix.)

    You’re welcome to change the name of your source files from the desktop. On Mac OS X and Windows, however, the desktop interface often hides file extensions, so check the properties of the file afterward to make sure you got the name you expected.

    In a convenient location (e.g., your home directory or the desktop) create a new directory for your project called tutorial. In this new directory, save your DrRacket file as poem.html.pp.

    "/path/to/tutorial/poem.html.pp"
    #lang pollen
     
    The margin is 42em.
    The border is red.
    The padding is 15em.
    The border is too.

    5.4 Using the project server

    The project server is a web server built into Pollen. Just as DrRacket lets you run individual files and see if they work as you expect, the project server lets you preview and test your project as a real website. While working on your Pollen project, you may find it convenient to have DrRacket open on half your screen, and on the other half, a web browser pointing at the project server.

    “Why can’t I just open the HTML files directly in my browser?” If you want to keep making web pages the way we did in 1996, go ahead. But that approach has several shortcomings. First, when you open files directly in your browser, you’re cruising the local filesystem, and absolute URLs (the kind that start with a /) won’t work. Second, if you want to test your website on devices other than your own machine — well, you can’t. Third, you have to render your HTML files in advance, whereas the project server is clever about doing this dynamically.

    So use the project server.

    A note about security. The project server isn’t intended for real-world use, but rather as a development tool. That said, once you start the project server, it’s an actual web server running on your machine, and it will respond to requests from any computer. If you want to limit traffic to your local network, or certain machines on your local network, it’s your job — not mine — to configure your firewall or other network security measures accordingly.

    5.4.1 Starting the project server with raco pollen

    Before we start the project server, a word about the raco pollen command.

    When you installed Racket, Racket installed a utility program called raco. This name is short for Racket command, and raco acts as a hub for — you guessed it — Racket commands. You used it when you first installed Pollen:

    > raco pkg install pollen

    The first argument after raco is the subcommand. For instance, raco pkg ... lets you install, update, and remove packages like so:

    > raco pkg update pollen

    > raco pkg remove pollen

    Likewise, raco pollen lets you issue commands relevant to Pollen, like starting the project server. (See Using raco pollen for a full description of available commands.)

    Now we’ll start the project server. Go to your command line and enter the following:

    > cd /path/to/tutorial

    > raco pollen start

    Windows users, I’ll trust you to convert raco into the appropriate command for your system — assuming defaults, it’s likely to be "C:\Program Files\Racket\raco" (include the surrounding quotes in the command).

    After a moment, you’ll see a startup message like this:

    Welcome to Pollen 0.001 (Racket 6.x.x.x)

    Project root is /path/to/tutorial

    Project server is http://localhost:8080 (Ctrl-C to exit)

    Project dashboard is http://localhost:8080/index.ptree

    Ready to rock

    Project root means the directory that the project server was started in, and which it’s treating as its root directory. Any absolute URLs (i.e., those beginning with /) will resolve into this directory. So a URL like /styles.css will impliedly become /path/to/tutorial/styles.css.

    If you use the bare command raco pollen start, the project server will start in the current directory. But if you want to start the project server elsewhere, you can add that directory as an argument like this:

    > raco pollen start /some/other/path

    The next line of the startup message tells you that the web address of the project server is http://localhost:8080. This is the address you put into your web browser to test your project. If you’re unfamiliar with this style of URL, localhost refers to your own machine, and 8080 is the network port where the project server will respond to browser requests.

    If you want to access the project server from a different machine, you can’t use localhost. But you can use the IP address of the machine running the project server (e.g., http://192.168.1.10:8080) or any name for that machine available through local DNS (e.g., http://mb-laptop:8080).

    Though port 8080 is the default, you can start the project server on any port you like by adding it as an argument to raco pollen start:

    > raco pollen start /path/to/tutorial

    > raco pollen start /path/to/tutorial 8088

    You can also change the default port by altering world:default-port, or parameterizing it with world: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 . shorthand:

    > cd /path/to/tutorial

    > raco pollen start 8088

    /path/to/tutorial/8088 is not a directory

    > raco pollen start . 8088

    Welcome to Pollen 0.001 (Racket 6.x.x.x) ...

    You can run multiple project servers simultaneously. Just start them on different ports so they don’t conflict with each other.

    Your terminal window will report status and error messages from the project server as it runs. Use Ctrl-C to stop the server.

    5.4.2 Using the dashboard

    For each directory in your project, starting at the top, the project server displays a dashboard in your web browser. The dashboard gives you an overview of the files in the directory, and links to view them.

    The address of the top-level dashboard is http://localhost:8080/index.ptree. Other dashboards follow the same pattern (e.g., http://localhost:8080/path/to/dir/index.ptree.)

    Note that the dashboard is not at http://localhost:8080/ or its equivalent, http://localhost:8080/index.html. Why? So it doesn’t interfere with any index.html that you may want to put in your project.

    Thus, index.ptree. The .ptree extension is short for pagetree. In Pollen, a pagetree is a hierarchical list of pages. We’ll do more with pagetrees in a later tutorial. For now, just be aware that to generate the dashboard, the project server will first look for an actual index.ptree file in each directory. If it doesn’t find one, it will generate a pagetree from a listing of files in the directory.

    Let’s look at the root-level dashboard for our project. First, make sure your project server is running:

    > cd /path/to/tutorial

    > raco pollen start

    Then, in your web browser, visit http://localhost:8080/index.ptree.

    You should see something like this:

    The top line tells us that we’re in the root directory of the project. We didn’t make an explicit index.ptree file, so the project server just shows us a directory listing.

    5.4.3 Source files in the dashboard

    We see the only file, poem.html.pp. Note that the .pp extension is grayed out. The dashboard automatically consolidates references to source and output files into a single entry. What this entry says is “The directory contains a source file in .pp format for the output file poem.html.”

    Every source-file entry in the dashboard has three links. The first link is attached to the filename itself, and takes you to a preview of the output file. If the output file doesn’t yet exist — as is the case here — it will be dynamically rendered. (This is true whether you click its name in the dashboard, or link to it from another page.) So click the filename. You’ll see in your web browser:

    The margin is 42em. The border is red. The padding is 15em. The border is too.

    Granted, this is a boring web page. The main point here is that you’re seeing the output from your source file, which didn’t exist before. Notice that the address bar says http://localhost:8080/poem.html, not poem.html.pp. And if you look in your tutorial directory, you’ll see a new file called poem.html.

    In other words, when you clicked on the filename link in the dashboard, Pollen rendered the output file from your source file and saved it in your project directory. As promised earlier, the name of the output file (poem.html) is the name of the source file (poem.html.pp) minus the Pollen extension (.pp).

    If you go back to the dashboard and click on the filename link again, you’ll see the same output file. If the source file hasn’t changed, Pollen will just show you the output file that’s already been rendered.

    But if you like, open your poem.html.pp source file in DrRacket, edit the first two lines, and save the file:

    #lang pollen
     
    The cave is pitch black.
    Look out for the grue.
    The padding is 15em.
    The border is too.

    Go back to the dashboard and click on the filename. This time, you’ll see:

    The cave is pitch black. Look out for the grue. The padding is 15em. The border is too.

    Here, Pollen notices that the source file has changed, so it refreshes the output file. This makes it convenient to work between DrRacket and your web browser, editing source and then reloading to see the changes.

    The other two links in the dashboard are labeled in and out.

    The link labeled in will display the contents of the source file:

    #lang pollen
     
    The cave is pitch black.
    Look out for the grue.
    The padding is 15em.
    The border is too.

    The link labeled out will display the contents of the output file (just like the “view source” option in your web browser):

    The cave is pitch black.

    Look out for the grue.

    The padding is 15em.

    The border is too.

    For now, the files are identical except for the #lang line. But let’s change that.

    5.5 Working with the preprocessor

    Pollen can operate in several processing modes. One of these is preprocessor mode. A preprocessor is a tool for making systematic, automated changes to a file, often in contemplation of further processing (hence the pre-). You can use the Pollen preprocessor this way. Or you can just use it on its own, and leave your files in a finished state.

    That’s how we’ll use it in this tutorial. We’ll build out our poem.html.pp source file so that it exits the preprocessor as a legit HTML file.

    5.5.1 Setting up a preprocessor source file

    The file extension of a Pollen source file tells Pollen what kind of processing to apply to it. The “.pp” file extension stands for “Pollen preprocessor.” You can use the preprocessor with any text-based file by: +
    • inserting #lang pollen as the first line,

    • adding the .pp file extension,

    • running it through Pollen.

    For more about the Pollen processing modes and how to invoke them, see File.

    “The preprocessor be used with any kind of text-based file?” Right. “But how?” The preprocessor reads the source file, handles any Pollen commands it finds, and lets the rest of the content pass through untouched. To the preprocessor, it’s all just text data. It doesn’t care whether that text represents HTML, CSS, JavaScript, or even TI-BASIC.

    One caveat: because the preprocessor only deals with text, the Pollen commands you use in the preprocessor also have to produce text. Moreover, Pollen doesn’t enforce the syntax rules of the underlying file — that’s your responsibility. For instance, Pollen won’t stop you from doing nonsensical things like this:

    "bad-poem.html.pp"
    #lang pollen
     
    The cave is pitch black.
    Look out for the grue.
    (insert-mp3-recording-of-scream)

    Pollen will fulfill your request, but the result won’t be valid HTML, because you can’t simply drop binary data in the middle of an HTML file. To paraphrase Mr. Babbage — garbage in, garbage out.

    I’ve encouraged you to mess with the source file, but let’s return it to its original state:

    "/path/to/tutorial/poem.html.pp"
    #lang pollen
     
    The margin is 42em.
    The border is red.
    The padding is 15em.
    The border is too.

    This file has #lang pollen as the first line, and .pp as the file extension, so it meets the minimum requirements for the preprocessor.

    5.5.2 Creating valid HTML output

    Let’s update our source so it produces valid HTML. Edit the source as follows:

    "/path/to/tutorial/poem.html.pp"
    #lang pollen
    <!DOCTYPE html>
    <html>
    <body>
    <pre>
    The margin is 42em.
    The border is red.
    The padding is 15em.
    The border is too.
    </pre>
    </body>
    </html>

    Return to the project server and view http://localhost:8080/poem.html. Earlier, the output looked like this:

    The margin is 42em. The border is red. The padding is 15em. The border is too.

    But now, because of the <pre> tag, the poem will appear in a monospaced font, and the line breaks will be preserved:

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    As before, because the source has changed, Pollen refreshes the output file. From the dashboard, you can use the in and out links to inspect the source and output.

    This is now a valid HTML page.

    5.5.3 Adding commands

    I mentioned that the preprocessor reads the file and handles any Pollen commands it finds. But our source file doesn’t have any commands yet. Let’s add some.

    Pollen commands can be embedded in your source file using one of two modes: Racket mode or text mode. We’ll try text mode in a later tutorial. For now, we’ll use Racket mode.

    To make a Racket-mode Pollen command, just take any Racket expression and put the lozenge character in front of it. For instance, these are valid Racket expressions:

    #lang racket
    (define inner 2)
    (define edge (* inner 4))
    (define color "blue")

    And these are the equivalent commands in Pollen:

    #lang pollen
    (define inner 2)
    (define edge (* inner 4))
    (define color "blue")

    How to type a lozenge:
    Mac: option + shift + V
    Windows: holding down alt, type 9674 on the num pad -
    Ubuntu: ctrl + shift + U, then 25CA

    5.5.4 Racket basics (if you’re not familiar)

    “But how am I supposed to know Racket?” You don’t. So we’ll start now. Here are the five basic rules of Racket:

    1. The core building block of Racket is the expression. An expression can be a value (like 2 or "blue"), a variable (like edge), or a function call (like (* inner 4)).

    2. Every expression is evaluated to produce a value.

    3. A variable evaluates to whatever value it holds (so inner would become 2). A function call evaluates to its return value (so (+ 1 1) would become 2).

    4. Function calls go between parentheses. Unlike most languages, the function name comes first, followed by its arguments (so it’s (* inner 4), not (inner * 4)). This is called prefix notation.

    5. Expressions can contain recursively nested expressions. Thus, (* inner 4) could be written (* inner (+ 2 2)) or (* inner (+ (+ 1 1) (+ 1 1))).

    Newcomers to Racket often gripe about prefix notation and the parentheses. If you need to get it out of your system, go ahead. Keep in mind, however, that it’s not some peculiar affectation, but rather a necessary consequence of rule #1. As you’ll come to learn, rule #1 is where the magic happens.

    That should tell you enough to infer what’s going on in the Pollen commands above:

    ◊(define inner 2)

    ◊; create a variable 'inner' that holds the value 2

    ◊(define edge (* inner 4))

    ◊; create a variable 'edge' that's four times the value of 'inner'

    ◊(define color "blue")

    ◊; create a variable 'color' that holds the value "blue"

    To learn more about Racket syntax, consider a detour through the excellent Quick: An Introduction to Racket with Pictures.

    5.5.5 Defining variables with commands

    Let’s use commands to define variables that will hold some values for our page. First, add a <head> tag to your source file, and three commmands to define three variables:

    "/path/to/tutorial/poem.html.pp"

    #lang pollen

    <!DOCTYPE html>

    <html>

    <head>

    ◊(define inner 2)

    ◊(define edge (* inner 4))

    ◊(define color "blue")

    </head>

    <body>

    <pre>

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    </pre>

    </body>

    </html>

    Then look at http://localhost:8080/poem.html again. Does it look the same? Not a trick question — it should. If you click the Out link on the dashboard, you’ll see this:

    <!DOCTYPE html>

    <html>

    <head>

     

     

     

    </head>

    <body>

    <pre>

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    </pre>

    </body>

    </html>

    What’s happening here? Our ◊(define ...) commands just define variables, so they don’t evaluate to any value. Instead, we get blank lines. So far, so good.

    5.5.6 Inserting values from variables

    To insert the value of a variable in our file, we use the command ◊|variable-name|. Let’s do that now:

    "/path/to/tutorial/poem.html.pp"

    #lang pollen

    <!DOCTYPE html>

    <html>

    <head>

    ◊(define inner 2)

    ◊(define edge (* inner 4))

    ◊(define color "blue")

    </head>

    <body>

    <pre>

    The margin is ◊|edge|em.

    The border is ◊|color|.

    The padding is ◊|inner|em.

    The border is too.

    </pre>

    </body>

    </html>

    Here, we’re replacing three values in the poem with the variables containing those values — ◊|edge|, ◊|color|, and ◊|inner|. Reload the file in the project server, and you’ll see:

    The margin is 8em.

    The border is blue.

    The padding is 2em.

    The border is too.

    Hey, look at that — the text of the poem changed. Now it even rhymes.

    If you like, in the source file, edit the variable definitions with different values and reload the page in the project server. The page will be rendered afresh with the new values. In particular, if you update inner, you’ll also see edge change, since its value depends on inner.

    5.5.7 Inserting variables within CSS

    Our poem makes claims about the margin, border, and padding of the page that aren’t yet true. To fix this, we’ll rely on the same basic technique of inserting variables into our HTML file. But instead of putting them in the <body> of the page, we’ll put them in a CSS <style> tag.

    Update the <head> section of the page with a new <style> tag that defines a style for pre like so, using our variables for the relevant values:

    "/path/to/tutorial/poem.html.pp"

    #lang pollen

    <!DOCTYPE html>

    <html>

    <head>

    ◊(define inner 2)

    ◊(define edge (* inner 4))

    ◊(define color "blue")

    <style type="text/css">

    pre {

        margin: ◊|edge|em;

        border: ◊|inner|em solid ◊|color|;

        padding: ◊|inner|em;

    }

    </style>

    </head>

    <body>

    <pre>

    The margin is ◊|edge|em.

    The border is ◊|color|.

    The padding is ◊|inner|em.

    The border is too.

    </pre>

    </body>

    </html>

    Notice that we’re using the same ◊|variable-name| pattern as before to insert the variable values.

    What do we expect to see? We expect that the padding and border will be 2em wide, because inner is 2. We expect the margin to be 8em, because it’s equal to edge, which is inner multiplied by 4. And we expect the color of the border to be "blue", because that’s the value of the variable color.

    And indeed, when you reload the file in the project server, you’ll see exactly that:

    As before, if you edit the values of the variables in the source file and reload in the project server, you’ll see both the text and the layout change.

    5.6 First tutorial complete

    This was a sneaky tutorial. The HTML page we made was very simple, but in building it, we covered many important points about how Pollen works.

    Feel free to go back and experiment with what you’ve learned. The next tutorial will assume that you’re comfortable with all the material here.

     
    \ No newline at end of file +
    Ubuntu: ctrl + shift + U, then 25CA

    5.5.4 Racket basics (if you’re not familiar)

    “But I’ve never used Racket.” Today, you start. Here are the five basic rules of Racket:

    1. The core building block of Racket is the expression. An expression can be a value (like 2 or "blue"), a variable (like edge), or a function call (like (* inner 4)).

    2. Every expression is evaluated to produce a value.

    3. A variable evaluates to whatever value it holds (so inner would become 2). A function call evaluates to its return value (so (+ 1 1) would become 2).

    4. Function calls go between parentheses. Unlike most languages, the function name comes first, followed by its arguments (so it’s (* inner 4), not (inner * 4)). This is called prefix notation.

    5. Expressions can contain recursively nested expressions. Thus, (* inner 4) could be written (* inner (+ 2 2)) or (* inner (+ (+ 1 1) (+ 1 1))).

    Newcomers to Racket often gripe about prefix notation and the parentheses. If you need to get it out of your system, go ahead. Keep in mind, however, that it’s not some peculiar affectation, but rather a necessary consequence of rule #1. As you’ll come to learn, rule #1 is where the magic happens.

    That should tell you enough to infer what’s going on in the Pollen commands above:

    #lang pollen
    (define inner 2)
    ; create a variable 'inner' that holds the value 2
    (define edge (* inner 4))
    ; create a variable 'edge' that's four times the value of 'inner'
    (define color "blue")
    ; create a variable 'color' that holds the value "blue"

    To learn more about Racket syntax, consider a detour through the excellent Quick: An Introduction to Racket with Pictures.

    5.5.5 Defining variables with commands

    Let’s use commands to define variables that will hold some values for our page. First, add a <head> tag to your source file, and three commmands to define three variables:

    "/path/to/tutorial/poem.html.pp"
    #lang pollen
     
    <!DOCTYPE html>
    <html>
    <head>
    (define inner 2)
    (define edge (* inner 4))
    (define color "blue")
    </head>
    <body>
    <pre>
    The margin is 42em.
    The border is red.
    The padding is 15em.
    The border is too.
    </pre>
    </body>
    </html>

    Then look at http://localhost:8080/poem.html again. Does it look the same? Not a trick question — it should. If you click the Out link on the dashboard, you’ll see this:

    <!DOCTYPE html>

    <html>

    <head>

     

     

     

    </head>

    <body>

    <pre>

    The margin is 42em.

    The border is red.

    The padding is 15em.

    The border is too.

    </pre>

    </body>

    </html>

    What’s with the blank lines? Don’t panic — our ◊(define ...) commands define variables, so they don’t evaluate to any value. Instead, we get blank lines. So far, so good.

    5.5.6 Inserting values from variables

    To insert the value of a variable in our file, we use the command ◊|variable-name|. Let’s do that now:

    "/path/to/tutorial/poem.html.pp"
    #lang pollen
     
    <!DOCTYPE html>
    <html>
    <head>
    (define inner 2)
    (define edge (* inner 4))
    (define color "blue")
    </head>
    <body>
    <pre>
    The margin is ◊|edge|em.
    The border is ◊|color|.
    The padding is ◊|inner|em.
    The border is too.
    </pre>
    </body>
    </html>

    Here, we’re replacing three values in the poem with the variables containing those values — ◊|edge|, ◊|color|, and ◊|inner|. Reload the file in the project server, and you’ll see:

    The margin is 8em.

    The border is blue.

    The padding is 2em.

    The border is too.

    Hey, look at that — the text of the poem changed. Now it even rhymes.

    If you like, in the source file, edit the variable definitions with different values and reload the page in the project server. The page will be rendered afresh with the new values. In particular, if you update inner, you’ll also see edge change, since its value depends on inner.

    5.5.7 Inserting variables within CSS

    Our poem makes claims about the margin, border, and padding of the page that aren’t yet true. To fix this, we’ll rely on the same basic technique of inserting variables into our HTML file. But instead of putting them in the <body> of the page, we’ll put them in a CSS <style> tag.

    Update the <head> section of the page with a new <style> tag that defines a style for pre like so, using our variables for the relevant values:

    "/path/to/tutorial/poem.html.pp"
    #lang pollen
     
    <!DOCTYPE html>
    <html>
    <head>
    (define inner 2)
    (define edge (* inner 4))
    (define color "blue")
    <style type="text/css">
    pre {
        margin: ◊|edge|em;
        border: ◊|inner|em solid ◊|color|;
        padding: ◊|inner|em;
    }
    </style>
    </head>
    <body>
    <pre>
    The margin is ◊|edge|em.
    The border is ◊|color|.
    The padding is ◊|inner|em.
    The border is too.
    </pre>
    </body>
    </html>

    Notice that we’re using the same ◊|variable-name| pattern as before to insert the variable values.

    What do we expect to see? We expect that the padding and border will be 2em wide, because inner is 2. We expect the margin to be 8em, because it’s equal to edge, which is inner multiplied by 4. And we expect the color of the border to be "blue", because that’s the value of the variable color.

    And indeed, when you reload the file in the project server, you’ll see exactly that:

    As before, if you edit the values of the variables in the source file and reload in the project server, you’ll see both the text and the layout change.

    5.6 First tutorial complete

    This was a sneaky tutorial. The HTML page we made was very simple, but in building it, we covered many important points about how Pollen works.

    Feel free to go back and experiment with what you’ve learned. The next tutorial will assume that you’re comfortable with all the material here.

     
    \ No newline at end of file diff --git a/doc/index.html b/doc/index.html index bc1ad05..c7531d1 100644 --- a/doc/index.html +++ b/doc/index.html @@ -1,3 +1,3 @@ -Pollen: the book is a program
    6.1.0.5

    Pollen: the book is a program

    Matthew Butterick <mb@mbtype.com>

     #lang pollen package: pollen

    Pollen is a publishing system that helps authors create beautiful and functional web-based books. Pollen includes tools for writing, designing, programming, testing, and publishing.

    I used Pollen to create my book Butterick’s Practical Typography. Sure, go take a look. Is it better than the last digital book you encountered? Yes it is. Would you like your book to look like that? If so, keep reading.

    At the core of Pollen is an argument: -
    • First, that digital books should be the best books we’ve ever had. So far, they’re not even close.

    • Second, that because digital books are software, an author shouldn’t think of a book as merely data. The book is a program.

    • Third, that the way we make digital books better than their predecessors is by exploiting this programmability.

    That’s what Pollen is for.

    Not that you need to be a programmer to use Pollen. On the contrary, the Pollen language is markup-based, so you can write & edit text naturally. But when you want to automate repetitive tasks, add cross-references, or pull in data from other sources, you can access a full programming language from within the text.

    That language is Racket. I chose Racket because while the idea for Pollen had been with me for several years, it simply wasn’t possible to build it with other languages. So if it’s unfamiliar to you, don’t panic. It was unfamiliar to me. Once you see what you can do with Pollen & Racket, you may be persuaded. I was.

    Or, if you can find a better digital-publishing tool, use that. But I’m never going back to the way I used to work.

        1 Installation

        2 Quick tour

          2.1 Creating a source file

          2.2 Running a source file

          2.3 Naming, saving, and rendering a source file

          2.4 The project server

          2.5 Intermission

          2.6 Pollen as a preprocessor

          2.7 Markdown mode

          2.8 Markup mode

          2.9 Templates

          2.10 PS for Scribble users

          2.11 The end of the beginning

        3 Backstory

          3.1 Web development and its discontents

          3.2 The better idea: a programming model

          3.3 “Now you have two problems”

          3.4 Rethinking the solution for digital books

          3.5 Enter Racket

          3.6 What is Pollen?

        4 The big picture

          4.1 The book is a program

          4.2 One language, multiple dialects

          4.3 Development environment

          4.4 A special data structure for HTML

          4.5 Pollen command syntax

          4.6 The preprocessor

          4.7 Templated source files

          4.8 Pagetrees

        5 First tutorial

          5.1 Prerequisites

          5.2 The relationship of Racket & Pollen

          5.3 Starting a new file in DrRacket

            5.3.1 Setting the #lang line

            5.3.2 Putting in the text of the poem

            5.3.3 Saving & naming your source file

          5.4 Using the project server

            5.4.1 Starting the project server with raco pollen

            5.4.2 Using the dashboard

            5.4.3 Source files in the dashboard

          5.5 Working with the preprocessor

            5.5.1 Setting up a preprocessor source file

            5.5.2 Creating valid HTML output

            5.5.3 Adding commands

            5.5.4 Racket basics (if you’re not familiar)

            5.5.5 Defining variables with commands

            5.5.6 Inserting values from variables

            5.5.7 Inserting variables within CSS

          5.6 First tutorial complete

        6 Second tutorial

          6.1 Prerequisites

          6.2 Prelude: my principled objection to Markdown

          6.3 Markdown in Pollen: two options

            6.3.1 Using Markdown with the preprocessor

            6.3.2 Authoring mode

            6.3.3 X-expressions

            6.3.4 Markdown authoring mode

          6.4 Templates

            6.4.1 The ->html function and the doc variable

            6.4.2 Making a custom template

            6.4.3 Inserting specific source data into templates

            6.4.4 Linking to an external CSS file

          6.5 Intermission

          6.6 Pagetrees

            6.6.1 Pagetree navigation

            6.6.2 Using the automatic pagetree

            6.6.3 Adding navigation links to the template with here

            6.6.4 Handling navigation boundaries with conditionals

            6.6.5 Making a pagetree file

            6.6.6 index.ptree & the project server

          6.7 Second tutorial complete

        7 Using raco pollen

          7.1 Making sure raco pollen works

          7.2 raco pollen

          7.3 raco pollen help

          7.4 raco pollen start

          7.5 raco pollen render

          7.6 raco pollen clone

        8 File formats

          8.1 Source formats

            8.1.1 Command syntax using ◊

            8.1.2 Any command is valid

            8.1.3 Standard exports

            8.1.4 Custom exports

            8.1.5 The project-require.rkt file

            8.1.6 Preprocessor (.pp extension)

            8.1.7 Markdown (.pmd extension)

            8.1.8 Markup (.pm extension)

            8.1.9 Pagetree (.ptree extension)

          8.2 Utility formats

            8.2.1 Scribble (.scrbl extension)

            8.2.2 Null (.p extension)

        9 ◊ command overview

          9.1 The golden rule

          9.2 The lozenge glyph (◊)

          9.3 The two command modes: text mode & Racket mode

            9.3.1 The command name

              9.3.1.1 Invoking tag functions

              9.3.1.2 Invoking other functions

              9.3.1.3 Inserting the value of a variable

              9.3.1.4 Inserting a comment

            9.3.2 The Racket arguments

            9.3.3 The text argument

          9.4 Further reading

        10 Module reference

          10.1 Cache

          10.2 Decode

            10.2.1 Block

            10.2.2 Typography

          10.3 File

          10.4 Pagetree

            10.4.1 Navigation

            10.4.2 Utilities

          10.5 Render

          10.6 Template

          10.7 Tag

          10.8 Top

          10.9 World

        11 Acknowledgments

        12 License & source code

        Index

     
    \ No newline at end of file +Pollen: the book is a program
    6.1.0.5

    Pollen: the book is a program

    Matthew Butterick <mb@mbtype.com>

     #lang pollen package: pollen

    Pollen is a publishing system that helps authors create beautiful and functional web-based books. Pollen includes tools for writing, designing, programming, testing, and publishing.

    I used Pollen to create my book Butterick’s Practical Typography. Sure, go take a look. Is it better than the last digital book you encountered? Yes it is. Would you like your book to look like that? If so, keep reading.

    At the core of Pollen is an argument: +
    • First, that digital books should be the best books we’ve ever had. So far, they’re not even close.

    • Second, that because digital books are software, an author shouldn’t think of a book as merely data. The book is a program.

    • Third, that the way we make digital books better than their predecessors is by exploiting this programmability.

    That’s what Pollen is for.

    Not that you need to be a programmer to use Pollen. On the contrary, the Pollen language is markup-based, so you can write & edit text naturally. But when you want to automate repetitive tasks, add cross-references, or pull in data from other sources, you can access a full programming language from within the text.

    That language is Racket. I chose Racket because while the idea for Pollen had been with me for several years, it simply wasn’t possible to build it with other languages. So if it’s unfamiliar to you, don’t panic. It was unfamiliar to me. Once you see what you can do with Pollen & Racket, you may be persuaded. I was.

    Or, if you can find a better digital-publishing tool, use that. But I’m never going back to the way I used to work.

        1 Installation

        2 Quick tour

          2.1 Creating a source file

          2.2 Running a source file

          2.3 Naming, saving, and rendering a source file

          2.4 The project server

          2.5 Intermission

          2.6 Pollen as a preprocessor

          2.7 Markdown mode

          2.8 Markup mode

          2.9 Templates

          2.10 PS for Scribble users

          2.11 The end of the beginning

        3 Backstory

          3.1 Web development and its discontents

          3.2 The better idea: a programming model

          3.3 “Now you have two problems”

          3.4 Rethinking the solution for digital books

          3.5 Enter Racket

          3.6 What is Pollen?

        4 The big picture

          4.1 The book is a program

          4.2 One language, multiple dialects

          4.3 Development environment

          4.4 A special data structure for HTML

          4.5 Pollen command syntax

          4.6 The preprocessor

          4.7 Templated source files

          4.8 Pagetrees

        5 First tutorial

          5.1 Prerequisites

          5.2 The relationship of Racket & Pollen

          5.3 Starting a new file in DrRacket

            5.3.1 Setting the #lang line

            5.3.2 Putting in the text of the poem

            5.3.3 Saving & naming your source file

          5.4 Using the project server

            5.4.1 Starting the project server with raco pollen

            5.4.2 Using the dashboard

            5.4.3 Source files in the dashboard

          5.5 Working with the preprocessor

            5.5.1 Setting up a preprocessor source file

            5.5.2 Creating valid HTML output

            5.5.3 Adding commands

            5.5.4 Racket basics (if you’re not familiar)

            5.5.5 Defining variables with commands

            5.5.6 Inserting values from variables

            5.5.7 Inserting variables within CSS

          5.6 First tutorial complete

        6 Second tutorial

          6.1 Prerequisites

          6.2 Prelude: my principled objection to Markdown

          6.3 Markdown in Pollen: two options

            6.3.1 Using Markdown with the preprocessor

            6.3.2 Authoring mode

            6.3.3 X-expressions

            6.3.4 Markdown authoring mode

          6.4 Templates

            6.4.1 The ->html function and the doc variable

            6.4.2 Making a custom template

            6.4.3 Inserting specific source data into templates

            6.4.4 Linking to an external CSS file

          6.5 Intermission

          6.6 Pagetrees

            6.6.1 Pagetree navigation

            6.6.2 Using the automatic pagetree

            6.6.3 Adding navigation links to the template with here

            6.6.4 Handling navigation boundaries with conditionals

            6.6.5 Making a pagetree file

            6.6.6 index.ptree & the project server

          6.7 Second tutorial complete

        7 Third tutorial

          7.1 Prerequisites

          7.2 Pollen markup vs. XML

            7.2.1 The XML problem

            7.2.2 What Pollen markup does differently

            7.2.3 “But I really need XML…”

          7.3 Writing with Pollen markup

            7.3.1 Creating a Pollen markup file

            7.3.2 Tags & tag functions

            7.3.3 Attributes

            7.3.4 What are custom tags good for?

            7.3.5 Semantic markup

            7.3.6 Format independence

            7.3.7 Using custom tags

            7.3.8 Choosing custom tags

          7.4 Tags are functions

            7.4.1 Attaching behavior to tags

            7.4.2 Notes for experienced programmers

              7.4.2.1 Point of no return

              7.4.2.2 Multiple input values & rest arguments

              7.4.2.3 Returning an X-expression

              7.4.2.4 Interpolating variables into strings

              7.4.2.5 Parsing attributes

          7.5 Intermission

          7.6 Organizing functions

            7.6.1 Using Racket’s function libraries

            7.6.2 The directory-require.rkt file

          7.7 Putting it all together

        8 Using raco pollen

          8.1 Making sure raco pollen works

          8.2 raco pollen

          8.3 raco pollen help

          8.4 raco pollen start

          8.5 raco pollen render

          8.6 raco pollen clone

        9 File formats

          9.1 Source formats

            9.1.1 Command syntax using ◊

            9.1.2 Any command is valid

            9.1.3 Standard exports

            9.1.4 Custom exports

            9.1.5 The directory-require.rkt file

            9.1.6 Preprocessor (.pp extension)

            9.1.7 Markdown (.pmd extension)

            9.1.8 Markup (.pm extension)

            9.1.9 Pagetree (.ptree extension)

          9.2 Utility formats

            9.2.1 Scribble (.scrbl extension)

            9.2.2 Null (.p extension)

        10 ◊ command overview

          10.1 The golden rule

          10.2 The lozenge glyph (◊)

          10.3 The two command modes: text mode & Racket mode

            10.3.1 The command name

              10.3.1.1 Invoking tag functions

              10.3.1.2 Invoking other functions

              10.3.1.3 Inserting the value of a variable

              10.3.1.4 Inserting a comment

            10.3.2 The Racket arguments

            10.3.3 The text argument

          10.4 Further reading

        11 Module reference

          11.1 Cache

          11.2 Decode

            11.2.1 Block

            11.2.2 Typography

          11.3 File

          11.4 Pagetree

            11.4.1 Navigation

            11.4.2 Utilities

          11.5 Render

          11.6 Template

          11.7 Tag

          11.8 Top

          11.9 World

        12 Acknowledgments

        13 License & source code

        Index

     
    \ No newline at end of file diff --git a/doc/mb.scss b/doc/mb.scss new file mode 100644 index 0000000..ceae050 --- /dev/null +++ b/doc/mb.scss @@ -0,0 +1,45 @@ +/* special styles for custom @fileblock function in mb-tools.rkt */ + +.noskip .refcolumn { + background: #998; + color: white; +} + +.noskip .refcontent p { + font-size: 110%; + line-height: 1.4; +} + +.fileblock .SIntrapara { + margin: 0 0 0 0; +} + +.fileblock .SCodeFlow { + padding-top: 0.7em; + margin-top: 0; +} + +.fileblock { + width: 90%; +} + +.fileblock_filetitle{ + background: #eee; + text-align:right; + padding: 0.15em; + border: 1px dotted black; + border-bottom: none; +} + +.terminal .SIntrapara, .browser .SIntrapara { + margin: 0 0 0 0; +} + +.terminal, .browser { + margin-bottom: 1em; + padding: 0.5em; + width: 88%; + background: #fcfcfc; + color: #666; +} + diff --git a/doc/quick-tour.html b/doc/quick-tour.html index 2083d93..929356e 100644 --- a/doc/quick-tour.html +++ b/doc/quick-tour.html @@ -1,5 +1,5 @@ -2 Quick tour
    6.1.0.5

    2 Quick tour

    2.1 Creating a source file

    Assuming you’ve installed Racket & Pollen, launch DrRacket.

    Open a new document. Change the top line to:

    #lang pollen

    The first line of every Pollen source file will start with #lang pollen.

    2.2 Running a source file

    Add a second line to your source file so it reads:

    #lang pollen
    Hello world

    Click the Run button. In the interactions window, you’ll see the result:

    Hello world

    Not bad. I think Pollen just won the Hello World Tournament.

    You can work with Pollen source files in any text editor. The key advantage of DrRacket is that you can preview the results by running the file.

    Try editing your source file:

    #lang pollen
    Goodbye Stranger
    Breakfast in America
    Take the Long Way Home

    You don’t have to use Supertramp song titles. Any text will do. When you click Run again, you’ll see whatever you typed:

    Goodbye Stranger
    Breakfast in America
    Take the Long Way Home

    We won’t do it a third time. You get the point — any plain text is valid within a Pollen source file, and gets printed as is. You never have to perform the incantations of typical programming languages:

    print "Hello world"

    document.write('Hello world');

    printf("Hello world");

    In Pollen, what you write is what you get.

    2.3 Naming, saving, and rendering a source file

    Save this file with the name hello.txt.pp in any convenient directory. The desktop is fine.

    Open a terminal window and issue two commands:

    > cd [directory containing your file]

    > raco pollen render hello.txt.pp

    After a moment, a new file will appear called hello.txt. Let’s see what’s in it:

    > cat hello.txt

    Goodbye Stranger

    Breakfast in America

    Take the Long Way Home

    You’ve just learned three things:

    • Pollen commands in the terminal begin with raco pollen, followed by a specific command (in this case render) and sometimes an argument (in this case hello.txt.pp).

    • The render command takes the ouput from your source file — meaning, the result you previewed in DrRacket in the previous step — and saves it to another file.

    • The name of the output file is the same as the source file, minus the Pollen source extension. So hello.txt.pp becomes hello.txt.

    Try editing the text in the hello.txt.pp source file and running raco pollen render hello.txt.pp again. The old hello.txt will be replaced with a new one showing your changes. And so you’ve learned a fourth thing:

    • Pollen works by rendering output files from source files. Output files can be overwritten. Therefore, you should only make edits to your source files.

    2.4 The project server

    You’ve just learned two ways to see the output of a Pollen source file — first, you ran it in DrRacket. Then, you rendered it to an output file.

    Now here’s a third: the Pollen project server. Here’s how you start it. Return to your terminal window and issue two commands:

    > cd [directory containing your hello.txt.pp file]

    > raco pollen start

    After a moment, you’ll see the startup message:

    Welcome to Pollen 0.001 (Racket 6.0.0.5)

    Project root is /path/to/your/directory

    Project server is http://localhost:8080 (Ctrl-C to exit)

    Project dashboard is http://localhost:8080/index.ptree

    Ready to rock

    Open a web browser and point it at http://localhost:8080/index.ptree. The top of the window will say Project root. Below that will be a listing of the files in the directory.

    Among them will be hello.txt, with a greyed-out .pp extension. Click on it, and you’ll be taken to http://localhost:8080/hello.txt, where you’ll see:

    Goodbye Stranger

    Breakfast in America

    Take the Long Way Home

    That’s the boring part. Here’s the good part. Leave the project server running. Open your source file again in DrRacket and edit it as follows:

    "hello.txt.pp"

    #lang pollen
    Mean Street
    Panama
    Hear About It Later

    Go back to your web browser and reload http://localhost:8080/hello.txt. Now you’ll see this:

    Mean Street

    Panama

    Hear About It Later

    Notice what happened — the Pollen project server dynamically regenerated the output file (hello.txt) from the source file (hello.txt.pp) after you edited the source. If you like, try making some more changes to hello.txt.pp, and reloading the browser to see the updates in hello.txt.

    2.5 Intermission

    That covers input & output. Now let’s circle back and look at what else you can do with Pollen (beyond the epic achievement of displaying plain text in a web browser).

    For the rest of this tutorial, I recommend keeping two windows on screen: a web-browser window pointed at your project server (the main URL is http://localhost:8080/index.ptree) and the DrRacket editing window.

    2.6 Pollen as a preprocessor

    A preprocessor is a tool for making systematic, automated changes to a source file before the main processing happens. A preprocessor can also be used to add programming logic to files that otherwise don’t support it.

    For instance, HTML. In DrRacket, create a new file called margin.html.pp in your project directory:

    "margin.html.pp"

    #lang pollen
    <body style="margin: 5em; border:1px solid black">
    5em is the inset.
    </body>

    The “.pp” file extension — which you saw before, with hello.txt.pp — stands for “Pollen preprocessor.” You can use the Pollen preprocessor with any text-based file by inserting #lang pollen as the first line, and adding the .pp file extension.

    But for now, go to your project dashboard and click on margin.html. You should see a black box containing the text “5em is the inset.”

    Let’s suppose you want to change the inset to 30%. Without a preprocessor, you’d have to search & replace each value. But with a preprocessor, you can move the inset value into a variable, and update it from that one location. So first, introduce a variable called my-inset by using the define command:

    "margin.html.pp"

    #lang pollen
    ◊define[my-inset]{30%}
    <body style="margin: 10em; border:1px solid black">
    10em is the inset.
    </body>

    The ◊ character is called a lozenge. In Pollen, the lozenge is a special character that marks anything Pollen should interpret as a command (rather than plain text).

    How to type a lozenge: +2 Quick tour

    6.1.0.5

    2 Quick tour

    2.1 Creating a source file

    Assuming you’ve installed Racket & Pollen, launch DrRacket.

    Open a new document. Change the top line to:

    #lang pollen

    The first line of every Pollen source file will start with #lang pollen.

    2.2 Running a source file

    Add a second line to your source file so it reads:

    #lang pollen
    Hello world

    Click the Run button. In the interactions window, you’ll see the result:

    Hello world

    Not bad. I think Pollen just won the Hello World Tournament.

    You can work with Pollen source files in any text editor. The key advantage of DrRacket is that you can preview the results by running the file.

    Try editing your source file:

    #lang pollen
    Goodbye Stranger
    Breakfast in America
    Take the Long Way Home

    You don’t have to use Supertramp song titles. Any text will do. When you click Run again, you’ll see whatever you typed:

    Goodbye Stranger
    Breakfast in America
    Take the Long Way Home

    We won’t do it a third time. You get the point — any plain text is valid within a Pollen source file, and gets printed as is. You never have to perform the incantations of typical programming languages:

    print "Hello world"

    document.write('Hello world');

    printf("Hello world");

    In Pollen, what you write is what you get.

    2.3 Naming, saving, and rendering a source file

    Save this file with the name hello.txt.pp in any convenient directory. The desktop is fine.

    Open a terminal window and issue two commands:

    > cd [directory containing your file]

    > raco pollen render hello.txt.pp

    After a moment, a new file will appear called hello.txt. Let’s see what’s in it:

    > cat hello.txt

    Goodbye Stranger

    Breakfast in America

    Take the Long Way Home

    You’ve just learned three things:

    • Pollen commands in the terminal begin with raco pollen, followed by a specific command (in this case render) and sometimes an argument (in this case hello.txt.pp).

    • The render command takes the ouput from your source file — meaning, the result you previewed in DrRacket in the previous step — and saves it to another file.

    • The name of the output file is the same as the source file, minus the Pollen source extension. So hello.txt.pp becomes hello.txt.

    Try editing the text in the hello.txt.pp source file and running raco pollen render hello.txt.pp again. The old hello.txt will be replaced with a new one showing your changes. And so you’ve learned a fourth thing:

    • Pollen works by rendering output files from source files. Output files can be overwritten. Therefore, you should only make edits to your source files.

    2.4 The project server

    You’ve just learned two ways to see the output of a Pollen source file — first, you ran it in DrRacket. Then, you rendered it to an output file.

    Now here’s a third: the Pollen project server. Here’s how you start it. Return to your terminal window and issue two commands:

    > cd [directory containing your hello.txt.pp file]

    > raco pollen start

    After a moment, you’ll see the startup message:

    Welcome to Pollen 0.001 (Racket 6.0.0.5)

    Project root is /path/to/your/directory

    Project server is http://localhost:8080 (Ctrl-C to exit)

    Project dashboard is http://localhost:8080/index.ptree

    Ready to rock

    Open a web browser and point it at http://localhost:8080/index.ptree. The top of the window will say Project root. Below that will be a listing of the files in the directory.

    Among them will be hello.txt, with a greyed-out .pp extension. Click on it, and you’ll be taken to http://localhost:8080/hello.txt, where you’ll see:

    Goodbye Stranger

    Breakfast in America

    Take the Long Way Home

    That’s the boring part. Here’s the good part. Leave the project server running. Open your source file again in DrRacket and edit it as follows:

    "hello.txt.pp"
    #lang pollen
    Mean Street
    Panama
    Hear About It Later

    Go back to your web browser and reload http://localhost:8080/hello.txt. Now you’ll see this:

    Mean Street

    Panama

    Hear About It Later

    Notice what happened — the Pollen project server dynamically regenerated the output file (hello.txt) from the source file (hello.txt.pp) after you edited the source. If you like, try making some more changes to hello.txt.pp, and reloading the browser to see the updates in hello.txt.

    2.5 Intermission

    That covers input & output. Now let’s circle back and look at what else you can do with Pollen (beyond the epic achievement of displaying plain text in a web browser).

    For the rest of this tutorial, I recommend keeping two windows on screen: a web-browser window pointed at your project server (the main URL is http://localhost:8080/index.ptree) and the DrRacket editing window.

    2.6 Pollen as a preprocessor

    A preprocessor is a tool for making systematic, automated changes to a source file before the main processing happens. A preprocessor can also be used to add programming logic to files that otherwise don’t support it.

    For instance, HTML. In DrRacket, create a new file called margin.html.pp in your project directory:

    "margin.html.pp"
    #lang pollen
    <body style="margin: 5em; border:1px solid black">
    5em is the inset.
    </body>

    The “.pp” file extension — which you saw before, with hello.txt.pp — stands for “Pollen preprocessor.” You can use the Pollen preprocessor with any text-based file by inserting #lang pollen as the first line, and adding the .pp file extension.

    But for now, go to your project dashboard and click on margin.html. You should see a black box containing the text “5em is the inset.”

    Let’s suppose you want to change the inset to 30%. Without a preprocessor, you’d have to search & replace each value. But with a preprocessor, you can move the inset value into a variable, and update it from that one location. So first, introduce a variable called my-inset by using the define command:

    "margin.html.pp"
    #lang pollen
    define[my-inset]{30%}
    <body style="margin: 10em; border:1px solid black">
    10em is the inset.
    </body>

    The ◊ character is called a lozenge. In Pollen, the lozenge is a special character that marks anything Pollen should interpret as a command (rather than plain text).

    How to type a lozenge:
    Mac: option + shift + V
    Windows: holding down alt, type 9674 on the num pad -
    Ubuntu: ctrl + shift + U, then 25CA

    The whole command ◊define[my-inset]{30%} means “create a variable called my-inset and give it the value 30%.”

    Then put the variable into the HTML like so, this time using the ◊ character with the variable name in the two places the value appears:

    "margin.html.pp"

    #lang pollen
    ◊define[my-inset]{30%}
    <body style="margin: ◊my-inset; border:1px solid black">
    ◊my-inset is the inset.
    </body>

    Now reload margin.html. You’ll see that the size of the margin has changed (because of the change to the style attribute) and so has the text of the HTML. If you like, try editing my-inset with different values and reloading the page. You can also try using define to create another variable (for instance, to change the color of the box border).

    Still, this is the tiniest tip of the iceberg. The Pollen preprocessor gives you access to everything in the Racket programming language — including math functions, text manipulation, and so on.

    2.7 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 <tedious>tags</tedious>? You can make Pollen do the heavy lifting by using it as a source decoder.

    For instance, Markdown mode. Markdown is a simplified notation system for HTML. You can use Pollen’s Markdown decoder by inserting #lang pollen as the first line, and adding the .pmd file extension.

    Try it. In DrRacket, create a file with the following lines and save it as downtown.html.pmd:

    "downtown.html.pmd"

    #lang pollen
     
    Pollen + Markdown
    -----------------
     
    + You **wanted** it  you _got_ it.
     
    + [search for Racket](https://google.com/search?q=racket)

    As before, go to the dashboard for the project server. This time, click the link for downtown.html. You’ll see something like this:

    Pollen + Markdown

    As usual, you’re welcome to edit downtown.html.pmd and then refresh the web browser to see the changes.

    In Markdown mode, you can still embed Pollen commands within the source as you did in preprocessor mode. Just keep in mind that your commands need to produce valid Markdown (as opposed to raw HTML). For instance, use define to create a variable called metal, and insert it into the Markdown:

    "downtown.html.pmd"

    #lang pollen
    ◊define[metal]{Plutonium}
     
    Pollen + ◊metal
    --------
     
    + You **wanted** ◊metal  you _got_ it.
     
    + [search for ◊metal](https://google.com/search?q=◊metal)

    Refresh downtown.html in the browser:

    Pollen + Plutonium

    Pollen is handling two tasks here: interpreting the commands in the source, and then converting the Markdown to HTML. But what if you wanted to use Pollen as a preprocessor that outputs a Markdown file? No problem — just change the source name from downtown.html.pmd to downtown.md.pp. Changing the extension from .pmd to .pp switches Pollen from Markdown mode back to preprocessor mode. And changing the base name from downtown.html to downtown.md updates the name of the output file.

    2.8 Markup mode

    If all you need to do is produce basic HTML, Markdown is great. But if you need to do semantic markup or other kinds of custom markup, it’s not flexible enough.

    In that case, you can use Pollen markup mode. To use Pollen markup, insert #lang pollen as the first line of your source file, and add a .pm file extension.

    Compared to Markdown mode, Pollen markup mode is wide open. Markdown mode gives you a limited set of formatting tools (i.e., the ones supported by Markdown). But in markup mode, you can use any tags you want. Markdown mode decodes the source in a fixed way (i.e., with the Markdown decoder). But markup mode lets you build any decoder you want.

    Let’s convert our Markdown example into Pollen markup. Marking up content is simple: insert the lozenge character () followed by the name of the tag (◊tag), followed by the content of the tag in curly braces (◊tag{content}). In DrRacket, create a new file called uptown.html.pm as follows:

    "uptown.html.pm"

    #lang pollen
     
    ◊headline{Pollen markup}
     
    ◊items{
     
    ◊item{You ◊strong{wanted} it  you ◊em{got} it.}
     
    ◊item{◊link["https://google.com/search?q=racket"]{search for Racket}}}

    Go to the project dashboard and click on uptown.html. You’ll see something like this:

    Pollen markup You wanted it — you got it. https://google.com/search?q=racketsearch for Racket

    That’s not right. What happened?

    We marked up the source using a combination of standard HTML tags (strong, em) and nonstandard ones (headline, items, item, link). This is valid Pollen markup. (In fact, if you look at the generated source, you’ll see that they didn’t disappear.) But since we’re targeting HTML, we need to convert our custom tags into valid HTML tags.

    For that, we’ll make a special file called project-require.rkt. This is a file in the standard Racket language that provides helper functions to decode the source. The definitions won’t make sense yet. But this is the quick tour, so all you need to do is copy, paste, and save:

    "project-require.rkt"

    #lang racket/base
    (require pollen/tag)
    (provide (all-defined-out))
    (define headline (make-tag-function 'h2))
    (define items (make-tag-function 'ul))
    (define item (make-tag-function 'li 'p))
    (define (link url text) `(a [[href ,url]] ,text))

    Return to the project dashboard and click on uptown.html. Now you’ll get the right result:

    Pollen markup

    Markup mode 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 multiple output formats, or handle complex page layouts — it’s the way to go.

    2.9 Templates

    The HTML pages we just made looked pretty dull. For the last stop on the quick tour, let’s fix that.

    Pollen source files that are written in Markdown or markup mode (i.e., .pmd or .pm files) are rendered with a template. A template is not a standalone Pollen source file. It’s a file of the output type — e.g., CSS, HTML, XML — where you put the stuff that needs to be consistent between output files. The template also contains template variables that mark where values from the Pollen source file should be inserted.

    When it needs a template, Pollen first looks for a file in the project directory named template.[output extension of source]. For uptown.html.pm, the output extension will be .html, thus Pollen will look for template.html.

    So let’s create template.html. Make a new file that with the following lines and save it to the same directory as uptown.html.pm:

    "template.html"

    <html><head><meta charset="UTF-8"/></head>

    <body style="background: #f6f6f6">

    <div style="background: white; margin: 3em;

    border:10px double gray; padding: 3em; font-size: 130%;">

    This file is ◊here

    <hr />

    ◊->html{◊doc}

    </div></body></html>

    This is a simple HTML file that should look familiar, except for the two template variables. The first, here, contains the name of the current source file. As before, the lozenge character marks it as a Pollen command rather than text, so you write it as ◊here. The other command, ◊->html{◊doc}, takes the content from the source file, which is contained in a variable called doc, and converts it to HTML with a Pollen function called ->html.

    Go back to your web browser and reload uptown.html. (Or downtown.html — both will work.) The page will be rendered with the new template.html. As before, you can edit the template or the source and the project server will dynamically update the output file.

    2.10 PS for Scribble users

    Pollen can also be used as a dynamic preview server for Scribble files. From your terminal, do the following:

    > cd [directory containing your Scribble files]

    > raco pollen start

    On the project dashboard, you’ll see your [filename].scrbl files listed as [filename].html. This may not represent the ultimate structure of your Scribble project — you may end up combining multiple Scribble source files into one HTML file, or making multiple HTML files from one Scribble source — but it’s handy for checking your work as you go.

    2.11 The end of the beginning

    Now you’ve seen the key features of Pollen. What do you think?

    • “So it’s like WordPress, but harder to use?” I was a happy WordPress user for several years. If you need a blog, it’s great. But the farther you get from blogs, the more it becomes like teaching an elephant to pirouette. And for those who like to solve problems with programming, PHP is, um, limited.

    • “What about pairing a Python template system and Python web server?” Good idea. I even tried it. But Python template systems don’t offer you Python — they offer you pidgin dialects that ain’t very Pythonic. Also, Python’s handing of markup-based data structures is cumbersome.

    • “Haven’t you heard of Jekyll?” Yes. If everything you need to write is expressible in Markdown, it’s great. If you need more than that, you’re stuck.

    • “Sounds a lot like LaTeX. Why not use that?” Also a good idea. LaTeX gets a lot of things right. But it wasn’t designed for web publishing.

    • “Eh, there are plenty of adequate options. Why should I learn a system written in Racket, which I’ve never used?” A salient objection. It’s also the question I asked myself before I committed to Racket. But publishing systems that are author- or designer-friendly tend to be programmer-hostile, and vice versa. Racket is the only language I found that could meet my requirements.

    But don’t take my word for it. The rest of this documentation will show you the cool, useful, and sophisticated things you can do with Pollen. If there’s another tool that suits you better, great. Keep in mind that I didn’t make Pollen because I’m a programmer. I’m a writer who wants to make electronic books that are better than the ones we have now. And for that, I needed a better tool.

     
    \ No newline at end of file +
    Ubuntu: ctrl + shift + U, then 25CA

    The whole command ◊define[my-inset]{30%} means “create a variable called my-inset and give it the value 30%.”

    Then put the variable into the HTML like so, this time using the ◊ character with the variable name in the two places the value appears:

    "margin.html.pp"
    #lang pollen
    define[my-inset]{30%}
    <body style="margin: ◊my-inset; border:1px solid black">
    my-inset is the inset.
    </body>

    Now reload margin.html. You’ll see that the size of the margin has changed (because of the change to the style attribute) and so has the text of the HTML. If you like, try editing my-inset with different values and reloading the page. You can also try using define to create another variable (for instance, to change the color of the box border).

    Still, this is the tiniest tip of the iceberg. The Pollen preprocessor gives you access to everything in the Racket programming language — including math functions, text manipulation, and so on.

    2.7 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 <tedious>tags</tedious>? You can make Pollen do the heavy lifting by using it as a source decoder.

    For instance, Markdown mode. Markdown is a simplified notation system for HTML. You can use Pollen’s Markdown decoder by inserting #lang pollen as the first line, and adding the .pmd file extension.

    Try it. In DrRacket, create a file with the following lines and save it as downtown.html.pmd:

    "downtown.html.pmd"
    #lang pollen
     
    Pollen + Markdown
    -----------------
     
    + You **wanted** it  you #,(racketfont "_got_") it.
     
    + [search for Racket](https://google.com/search?q=racket)

    As before, go to the dashboard for the project server. This time, click the link for downtown.html. You’ll see something like this:

    Pollen + Markdown

    As usual, you’re welcome to edit downtown.html.pmd and then refresh the web browser to see the changes.

    In Markdown mode, you can still embed Pollen commands within the source as you did in preprocessor mode. Just keep in mind that your commands need to produce valid Markdown (as opposed to raw HTML). For instance, use define to create a variable called metal, and insert it into the Markdown:

    "downtown.html.pmd"
    #lang pollen
    define[metal]{Plutonium}
     
    Pollen + metal
    --------
     
    + You **wanted** metal  you #,(racketfont "_got_") it.
     
    + [search for metal](https://google.com/search?q=◊metal)

    Refresh downtown.html in the browser:

    Pollen + Plutonium

    Pollen is handling two tasks here: interpreting the commands in the source, and then converting the Markdown to HTML. But what if you wanted to use Pollen as a preprocessor that outputs a Markdown file? No problem — just change the source name from downtown.html.pmd to downtown.md.pp. Changing the extension from .pmd to .pp switches Pollen from Markdown mode back to preprocessor mode. And changing the base name from downtown.html to downtown.md updates the name of the output file.

    2.8 Markup mode

    If all you need to do is produce basic HTML, Markdown is great. But if you need to do semantic markup or other kinds of custom markup, it’s not flexible enough.

    In that case, you can use Pollen markup mode. To use Pollen markup, insert #lang pollen as the first line of your source file, and add a .pm file extension.

    Compared to Markdown mode, Pollen markup mode is wide open. Markdown mode gives you a limited set of formatting tools (i.e., the ones supported by Markdown). But in markup mode, you can use any tags you want. Markdown mode decodes the source in a fixed way (i.e., with the Markdown decoder). But markup mode lets you build any decoder you want.

    Let’s convert our Markdown example into Pollen markup. Marking up content is simple: insert the lozenge character () followed by the name of the tag (◊tag), followed by the content of the tag in curly braces (◊tag{content}). In DrRacket, create a new file called uptown.html.pm as follows:

    "uptown.html.pm"
    #lang pollen
     
    headline{Pollen markup}
     
    items{
      item{You strong{wanted} it  you em{got} it.}
      item{link["https://google.com/search?q=racket"]{search for Racket}}
    }

    Go to the project dashboard and click on uptown.html. You’ll see something like this:

    Pollen markup You wanted it — you got it. https://google.com/search?q=racketsearch for Racket

    That’s not right. What happened?

    We marked up the source using a combination of standard HTML tags (strong, em) and nonstandard ones (headline, items, item, link). This is valid Pollen markup. (In fact, if you look at the generated source, you’ll see that they didn’t disappear.) But since we’re targeting HTML, we need to convert our custom tags into valid HTML tags.

    For that, we’ll make a special file called directory-require.rkt. This is a file in the standard Racket language that provides helper functions to decode the source. The definitions won’t make sense yet. But this is the quick tour, so all you need to do is copy, paste, and save:

    "directory-require.rkt"
    #lang racket/base
     
    (require pollen/tag)
    (provide (all-defined-out))
    (define headline (make-default-tag-function 'h2))
    (define items (make-default-tag-function 'ul))
    (define item (make-default-tag-function 'li 'p))
    (define (link url text) `(a [[href ,url]] ,text))

    Return to the project dashboard and click on uptown.html. Now you’ll get the right result:

    Pollen markup

    Markup mode 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 multiple output formats, or handle complex page layouts — it’s the way to go.

    2.9 Templates

    The HTML pages we just made looked pretty dull. For the last stop on the quick tour, let’s fix that.

    Pollen source files that are written in Markdown or markup mode (i.e., .pmd or .pm files) are rendered with a template. A template is not a standalone Pollen source file. It’s a file of the output type — e.g., CSS, HTML, XML — where you put the stuff that needs to be consistent between output files. The template also contains template variables that mark where values from the Pollen source file should be inserted.

    When it needs a template, Pollen first looks for a file in the project directory named template.[output extension of source]. For uptown.html.pm, the output extension will be .html, thus Pollen will look for template.html.

    So let’s create template.html. Make a new file that with the following lines and save it to the same directory as uptown.html.pm:

    "template.html"
    <html><head><meta charset="UTF-8"/></head>
    <body style="background: #f6f6f6">
    <div style="background: white; margin: 3em;
    border:10px double gray; padding: 3em; font-size: 130%;">
    This file is ◊here
    <hr />
    ◊->html{◊doc}
    </div></body></html>

    This is a simple HTML file that should look familiar, except for the two template variables. The first, here, contains the name of the current source file. As before, the lozenge character marks it as a Pollen command rather than text, so you write it as ◊here. The other command, ◊->html{◊doc}, takes the content from the source file, which is contained in a variable called doc, and converts it to HTML with a Pollen function called ->html.

    Go back to your web browser and reload uptown.html. (Or downtown.html — both will work.) The page will be rendered with the new template.html. As before, you can edit the template or the source and the project server will dynamically update the output file.

    2.10 PS for Scribble users

    Pollen can also be used as a dynamic preview server for Scribble files. From your terminal, do the following:

    > cd [directory containing your Scribble files]

    > raco pollen start

    On the project dashboard, you’ll see your [filename].scrbl files listed as [filename].html. This may not represent the ultimate structure of your Scribble project — you may end up combining multiple Scribble source files into one HTML file, or making multiple HTML files from one Scribble source — but it’s handy for checking your work as you go.

    2.11 The end of the beginning

    Now you’ve seen the key features of Pollen. What do you think?

    • “So it’s like WordPress, but harder to use?” I was a happy WordPress user for several years. If you need a blog, it’s great. But the farther you get from blogs, the more it becomes like teaching an elephant to pirouette. And for those who like to solve problems with programming, PHP is, um, limited.

    • “What about pairing a Python template system and Python web server?” Good idea. I even tried it. But Python template systems don’t offer you Python — they offer you pidgin dialects that ain’t very Pythonic. Also, Python’s handing of markup-based data structures is cumbersome.

    • “Haven’t you heard of Jekyll?” Yes. If everything you need to write is expressible in Markdown, it’s great. If you need more than that, you’re stuck.

    • “Sounds a lot like LaTeX. Why not use that?” Also a good idea. LaTeX gets a lot of things right. But it wasn’t designed for web publishing.

    • “Eh, there are plenty of adequate options. Why should I learn a system written in Racket, which I’ve never used?” A salient objection. It’s also the question I asked myself before I committed to Racket. But publishing systems that are author- or designer-friendly tend to be programmer-hostile, and vice versa. Racket is the only language I found that could meet my requirements.

    But don’t take my word for it. The rest of this documentation will show you the cool, useful, and sophisticated things you can do with Pollen. If there’s another tool that suits you better, great. Keep in mind that I didn’t make Pollen because I’m a programmer. I’m a writer who wants to make electronic books that are better than the ones we have now. And for that, I needed a better tool.

     
    \ No newline at end of file diff --git a/doc/raco-pollen.html b/doc/raco-pollen.html index 0e25e64..6492833 100644 --- a/doc/raco-pollen.html +++ b/doc/raco-pollen.html @@ -1,2 +1,2 @@ -7 Using raco pollen
    6.1.0.5

    7 Using raco pollen

    Racket provides centralized command-line options through raco (short for racket command, see raco: Racket Command-Line Tools).

    Once you install Pollen, you can access the following Pollen-specific commands through raco using the subcommand raco pollen.

    7.1 Making sure raco pollen works

    Open a terminal window and type:

    > raco pollen test

    If raco pollen is installed correctly, you’ll see:

    raco pollen is installed correctly

    But if you get:

    raco: Unrecognized command: pollen

    You’ll need to fix the problem before proceeding, most likely by reinstalling Pollen (see Installation).

    Pro tip: I have an alias in my .bash_profile like so: alias polcom='raco pollen'

    7.2 raco pollen

    Same as raco pollen help.

    7.3 raco pollen help

    Displays a list of available commands.

    7.4 raco pollen start

    Starts the project server from the current directory using the default port, which is the value of the parameter world:current-server-port (by default, port 8080).

    This command can be invoked with two optional arguments.

    raco pollen start path will start the project server in path rather than the current directory.

    > raco pollen start ~/path/to/project/

    raco pollen start path port will start the project server in path using port rather than world:current-server-port. This is useful if you want to have multiple project servers running simultaneously.

    > raco pollen start ~/path/to/project/

    > raco pollen start ~/path/to/project/scribblings 8088

    If you want to start in the current directory but with a different port, use . as the path:

    > raco pollen start . 8088

    7.5 raco pollen render

    Renders all preprocessor source files and then all pagetree files found in the current directory.

    This command can be invoked with extra arguments.

    raco pollen render directory will render the preprocessor source files and pagetree files in the specified directory.

    Alternatively, the command can take a variable number of path arguments. raco pollen render path... will render only the paths specified in path.... Consistent with the usual command-line idiom, this can be a single path, a list of paths, or a pattern:

    > raco pollen render foo.html.pm

    > raco pollen render foo.html.pm bar.html.pm zam.css.pp

    > raco pollen render *.html.pm

    7.6 raco pollen clone

    Makes a copy of the project directory on the desktop, and removes any source files or other Pollen-related files.

    raco pollen clobe directory-path will perform the same copying and filtering, but using directory-path as the destination rather than the desktop.

     
    \ No newline at end of file +8 Using raco pollen
    6.1.0.5

    8 Using raco pollen

    Racket provides centralized command-line options through raco (short for racket command, see raco: Racket Command-Line Tools).

    Once you install Pollen, you can access the following Pollen-specific commands through raco using the subcommand raco pollen.

    8.1 Making sure raco pollen works

    Open a terminal window and type:

    > raco pollen test

    If raco pollen is installed correctly, you’ll see:

    raco pollen is installed correctly

    But if you get:

    raco: Unrecognized command: pollen

    You’ll need to fix the problem before proceeding, most likely by reinstalling Pollen (see Installation).

    Pro tip: I have an alias in my .bash_profile like so: alias polcom='raco pollen'

    8.2 raco pollen

    Same as raco pollen help.

    8.3 raco pollen help

    Displays a list of available commands.

    8.4 raco pollen start

    Starts the project server from the current directory using the default port, which is the value of the parameter world:current-server-port (by default, port 8080).

    This command can be invoked with two optional arguments.

    raco pollen start path will start the project server in path rather than the current directory.

    > raco pollen start ~/path/to/project/

    raco pollen start path port will start the project server in path using port rather than world:current-server-port. This is useful if you want to have multiple project servers running simultaneously.

    > raco pollen start ~/path/to/project/

    > raco pollen start ~/path/to/project/scribblings 8088

    If you want to start in the current directory but with a different port, use . as the path:

    > raco pollen start . 8088

    8.5 raco pollen render

    Renders all preprocessor source files and then all pagetree files found in the current directory.

    This command can be invoked with extra arguments.

    raco pollen render directory will render the preprocessor source files and pagetree files in the specified directory.

    Alternatively, the command can take a variable number of path arguments. raco pollen render path... will render only the paths specified in path.... Consistent with the usual command-line idiom, this can be a single path, a list of paths, or a pattern:

    > raco pollen render foo.html.pm

    > raco pollen render foo.html.pm bar.html.pm zam.css.pp

    > raco pollen render *.html.pm

    8.6 raco pollen clone

    Makes a copy of the project directory on the desktop, and removes any source files or other Pollen-related files.

    raco pollen clobe directory-path will perform the same copying and filtering, but using directory-path as the destination rather than the desktop.

     
    \ No newline at end of file diff --git a/doc/reader.html b/doc/reader.html index b99261f..3b88c5b 100644 --- a/doc/reader.html +++ b/doc/reader.html @@ -1,28 +1,28 @@ -9 ◊ command overview
    6.1.0.5

    9 ◊ command overview

    9.1 The golden rule

    Pollen uses a special character — the lozenge, which looks like this: ◊ — to mark commands within a Pollen source file. So when you put a ◊ in your source, whatever comes next will be treated as a command. If you don’t, it will just be interpreted as plain text.

    9.2 The lozenge glyph (◊)

    I chose the lozenge as the command marker because a) it appears in almost every font, b) it’s barely used in ordinary typesetting, c) it’s not used in any programming language that I know of, and d) its shape and color allow it to stand out easily in code without being distracting.

    Here’s how you type it:

    Mac: option + shift + V +10 ◊ command overview

    6.1.0.5

    10 ◊ command overview

    10.1 The golden rule

    Pollen uses a special character — the lozenge, which looks like this: ◊ — to mark commands within a Pollen source file. So when you put a ◊ in your source, whatever comes next will be treated as a command. If you don’t, it will just be interpreted as plain text.

    10.2 The lozenge glyph (◊)

    I chose the lozenge as the command marker because a) it appears in almost every font, b) it’s barely used in ordinary typesetting, c) it’s not used in any programming language that I know of, and d) its shape and color allow it to stand out easily in code without being distracting.

    Here’s how you type it:

    Mac: option + shift + V
    Windows: holding down alt, type 9674 on the num pad
    -Ubuntu: ctrl + shift + U, then 25CA

    Still, if you don’t want to use the lozenge as your command marker, you can use something else. Set Pollen’s world:command-marker value to whatever character you want.

    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.

    But don’t knock the lozenge till you try it.

    9.3 The two command modes: text mode & Racket mode

    Pollen commands can be entered in one of two modes: text mode or Racket mode. Both modes start with a lozenge ():

     command name [ Racket arguments ... ] { text argument }
     ( Racket expression )

    Text-mode commands

    A text-mode command has the three possible parts after the :

    • The command name appears immediately after the . Typically it’s a short word.

    • The Racket arguments appear between square brackets. Pollen is partly an interface to the Racket programming language. These arguments are entered using Racket conventions — e.g., a string of text needs to be put in quotes as a "string of text". If you like programming, you’ll end up using these frequently. If you don’t, you won’t.

    • The text argument appears between braces (aka curly brackets). You can put any ordinary text here. Unlike with the Racket arguments, you don’t put quotes around the text.

    Each of the three parts is optional. You can also nest commands within each other. However:

    • You can never have spaces between the three parts.

    • Whatever parts you use must always appear in the order above.

    Here are a few examples of correct text-mode commands:

    #lang pollen
    variable-name
    tag{Text inside the tag.}
    tag['attr: "value"]{Text inside the tag}
    get-customer-id["Brennan Huff"]
    tag{His ID is get-customer-id["Brennan Huff"].}

    And some incorrect examples:

    #lang pollen
    tag {Text inside the tag.} ; space between first and second parts
    tag[Text inside the tag] ; text argument needs to be within braces
    tag{Text inside the tag}['attr: "value"] ; wrong order

    The next section describes each of these parts in detail.

    Racket-mode commands

    If you’re familiar with Racket expressions, you can use the Racket-mode commands to embed them within Pollen source files. It’s simple: any Racket expression can become a Pollen command by adding to the front. So in Racket, this code:

    #lang racket
    (define song "Revolution")
    (format "~a #~a" song (* 3 3))

    Can be converted to Pollen like so:

    #lang pollen
    (define song "Revolution")
    (format "~a #~a" song (* 3 3))

    And in DrRacket, they produce the same output:

    Revolution #9

    Beyond that, there’s not much to say about Racket mode — any valid expression you can write in Racket will also be a valid Racket-mode Pollen command.

    The relationship of text mode and Racket mode

    Even if you don’t plan to write a lot of Racket-mode commands, you should be aware that under the hood, Pollen is converting all commands in text mode to Racket mode. So a text-mode command that looks like this:

    ◊headline[#:size 'enormous]{Man Bites Dog!}

    Is actually being turned into a Racket-mode command like this:

    (headline #:size 'enormous "Man Bites Dog!")

    Thus a text-mode command is just an alternate way of writing a Racket-mode command. (More broadly, all of Pollen is just an alternate way of using Racket.)

    The corollary is that you can always write Pollen commands using whichever mode is more convenient or readable. For instance, the earlier example, written in the Racket mode:

    #lang pollen
    (define song "Revolution")
    (format "~a #~a" song (* 3 3))

    Can be rewritten using text mode:

    #lang pollen
    define[song]{Revolution}
    format["~a #~a" song (* 3 3)]

    And it will work the same way.

    9.3.1 The command name

    In Pollen, you’ll typically use the command name for one of four purposes:

    • To invoke a tag function.

    • To invoke another function.

    • To insert the value of a variable.

    • To insert a comment.

    9.3.1.1 Invoking tag functions

    By default, Pollen treats every command name as a tag function. As the name implies, a tag function creates a tagged X-expression with the command name as the tag, and the text argument as the content.

    #lang pollen
    strong{Fancy Sauce, $1}

    '(strong "Fancy Sauce, $1")

    To streamline markup, Pollen doesn’t restrict you to a certain set of tags, nor does it make you define your tag functions ahead of time. Just type a tag, and you can start using it.

    #lang pollen
    utterlyridiculoustagname{Oh really?}
    '(utterlyridiculoustagname "Oh really?")

    The one restriction is that you can’t invent names for tag functions that are already being used for other commands. For instance, map is a name permanently reserved by the Racket function map. It’s also a rarely-used HTML tag. But gosh, you really want to use it. Problem is, if you invoke it directly, Pollen will think you mean the other map:

    #lang pollen
    map{Fancy Sauce, $1}

    map: arity mismatch;
    +Ubuntu: ctrl + shift + U, then 25CA

    Still, if you don’t want to use the lozenge as your command marker, you can use something else. Set Pollen’s world:command-marker value to whatever character you want.

    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.

    But don’t knock the lozenge till you try it.

    10.3 The two command modes: text mode & Racket mode

    Pollen commands can be entered in one of two modes: text mode or Racket mode. Both modes start with a lozenge ():

     command name [ Racket arguments ... ] { text argument }
     ( Racket expression )

    Text-mode commands

    A text-mode command has the three possible parts after the :

    • The command name appears immediately after the . Typically it’s a short word.

    • The Racket arguments appear between square brackets. Pollen is partly an interface to the Racket programming language. These arguments are entered using Racket conventions — e.g., a string of text needs to be put in quotes as a "string of text". If you like programming, you’ll end up using these frequently. If you don’t, you won’t.

    • The text argument appears between braces (aka curly brackets). You can put any ordinary text here. Unlike with the Racket arguments, you don’t put quotes around the text.

    Each of the three parts is optional. You can also nest commands within each other. However:

    • You can never have spaces between the three parts.

    • Whatever parts you use must always appear in the order above.

    Here are a few examples of correct text-mode commands:

    #lang pollen
    variable-name
    tag{Text inside the tag.}
    tag['attr: "value"]{Text inside the tag}
    get-customer-id["Brennan Huff"]
    tag{His ID is get-customer-id["Brennan Huff"].}

    And some incorrect examples:

    #lang pollen
    tag {Text inside the tag.} ; space between first and second parts
    tag[Text inside the tag] ; text argument needs to be within braces
    tag{Text inside the tag}['attr: "value"] ; wrong order

    The next section describes each of these parts in detail.

    Racket-mode commands

    If you’re familiar with Racket expressions, you can use the Racket-mode commands to embed them within Pollen source files. It’s simple: any Racket expression can become a Pollen command by adding to the front. So in Racket, this code:

    #lang racket
    (define song "Revolution")
    (format "~a #~a" song (* 3 3))

    Can be converted to Pollen like so:

    #lang pollen
    (define song "Revolution")
    (format "~a #~a" song (* 3 3))

    And in DrRacket, they produce the same output:

    Revolution #9

    Beyond that, there’s not much to say about Racket mode — any valid expression you can write in Racket will also be a valid Racket-mode Pollen command.

    The relationship of text mode and Racket mode

    Even if you don’t plan to write a lot of Racket-mode commands, you should be aware that under the hood, Pollen is converting all commands in text mode to Racket mode. So a text-mode command that looks like this:

    ◊headline[#:size 'enormous]{Man Bites Dog!}

    Is actually being turned into a Racket-mode command like this:

    (headline #:size 'enormous "Man Bites Dog!")

    Thus a text-mode command is just an alternate way of writing a Racket-mode command. (More broadly, all of Pollen is just an alternate way of using Racket.)

    The corollary is that you can always write Pollen commands using whichever mode is more convenient or readable. For instance, the earlier example, written in the Racket mode:

    #lang pollen
    (define song "Revolution")
    (format "~a #~a" song (* 3 3))

    Can be rewritten using text mode:

    #lang pollen
    define[song]{Revolution}
    format["~a #~a" song (* 3 3)]

    And it will work the same way.

    10.3.1 The command name

    In Pollen, you’ll typically use the command name for one of four purposes:

    • To invoke a tag function.

    • To invoke another function.

    • To insert the value of a variable.

    • To insert a comment.

    10.3.1.1 Invoking tag functions

    By default, Pollen treats every command name as a tag function. As the name implies, a tag function creates a tagged X-expression with the command name as the tag, and the text argument as the content.

    #lang pollen
    strong{Fancy Sauce, $1}

    '(strong "Fancy Sauce, $1")

    To streamline markup, Pollen doesn’t restrict you to a certain set of tags, nor does it make you define your tag functions ahead of time. Just type a tag, and you can start using it.

    #lang pollen
    utterlyridiculoustagname{Oh really?}
    '(utterlyridiculoustagname "Oh really?")

    The one restriction is that you can’t invent names for tag functions that are already being used for other commands. For instance, map is a name permanently reserved by the Racket function map. It’s also a rarely-used HTML tag. But gosh, you really want to use it. Problem is, if you invoke it directly, Pollen will think you mean the other map:

    #lang pollen
    map{Fancy Sauce, $1}

    map: arity mismatch;
    the expected number of arguments does not match the given number
      given: 1
      arguments...:
    -    "Fancy Sauce, $1"

    What to do? Read on.

    9.3.1.2 Invoking other functions

    Though every command name starts out as a tag function, it doesn’t necessarily end there. You have two options for invoking other functions: defining your own , or invoking others from Racket.

    Defining your own functions

    Use the define command to create your own function for a command name. After that, when you use the command name, you’ll get the new behavior. For instance, recall this example showing the default tag-function behavior:

    #lang pollen
    strong{Fancy Sauce, $1}

    '(strong "Fancy Sauce, $1")

    We can define strong to do something else, like add to the text:

    #lang pollen
    (define (strong text) `(strong ,(format "Hey! Listen up! ~a" text)))
    strong{Fancy Sauce, $1}

    '(strong "Hey! Listen up! Fancy Sauce, $1")

    The replacement function has to accept any arguments that might get passed along, but it doesn’t have to do anything with them. For instance, this function definition won’t work because strong is going to get a text argument that it’s not defined to handle:

    #lang pollen
    (define (strong) '(fib "1 1 2 3 5 8 13 ..."))
    strong{Fancy Sauce, $1}

    strong: arity mismatch;
    +    "Fancy Sauce, $1"

    What to do? Read on.

    10.3.1.2 Invoking other functions

    Though every command name starts out as a tag function, it doesn’t necessarily end there. You have two options for invoking other functions: defining your own , or invoking others from Racket.

    Defining your own functions

    Use the define command to create your own function for a command name. After that, when you use the command name, you’ll get the new behavior. For instance, recall this example showing the default tag-function behavior:

    #lang pollen
    strong{Fancy Sauce, $1}

    '(strong "Fancy Sauce, $1")

    We can define strong to do something else, like add to the text:

    #lang pollen
    (define (strong text) `(strong ,(format "Hey! Listen up! ~a" text)))
    strong{Fancy Sauce, $1}

    '(strong "Hey! Listen up! Fancy Sauce, $1")

    The replacement function has to accept any arguments that might get passed along, but it doesn’t have to do anything with them. For instance, this function definition won’t work because strong is going to get a text argument that it’s not defined to handle:

    #lang pollen
    (define (strong) '(fib "1 1 2 3 5 8 13 ..."))
    strong{Fancy Sauce, $1}

    strong: arity mismatch;
    the expected number of arguments does not match the given number
      expected: 0
      given: 1
      arguments...:
    -    "Fancy Sauce, $1"

    Whereas in this version, strong accepts an argument called text, but then ignores it:

    #lang pollen
    (define (strong text) '(fib "1 1 2 3 5 8 13 ..."))
    strong{Fancy Sauce, $1}

    '(fib "1 1 2 3 5 8 13 ...")

    You can attach any behavior to a command name. As your project evolves, you can also update the behavior of a command name. In that way, Pollen commands become a set of hooks to which you can attach more elaborate processing.

    Using Racket functions

    You aren’t limited to functions you define. Any function from Racket, or any Racket library, can be invoked directly by using it as a command name. Here’s the function range, which creates a list of numbers:

    #lang pollen
    range[1 20]

    '(range 1 20)

    Hold on — that’s not what we want. Where’s the list of numbers? The problem here is that we didn’t explicitly import the racket/list library, which contains the definition for range. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without racket/list, Pollen just thinks we’re trying to use range as a tag function (and if we had been, then '(range 1 20) would’ve been the right result).

    We fix this by using the require command to bring in the racket/list library, which contains the range we want:

    #lang pollen
    (require racket/list)
    range[1 20]

    '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

    Of course, you can also invoke Racket functions indirectly, by attaching them to functions you define for command names:

    #lang pollen
    (require racket/list)
    (define (rick start finish) (range start finish))
    rick[1 20]

    '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

    Let’s return to the problem that surfaced in the last section — the fact that some command names can’t be used as tag functions because they’re already being used for other things. You can work around this by defining your own tag function with a non-conflicting name.

    For instance, suppose we want to use map as a tag even though Racket is using it for its own function called map. First, we invent a command name that doesn’t conflict. Let’s call it my-map. As you learned above, Pollen will treat a new command name as a tag function by default:

    #lang pollen
    my-map{How I would love this to be a map.}

    '(my-map "How I would love this to be a map.")

    But my-map is not the tag we want. We need to define my-map to be a tag function for map. We can do this with the Pollen helper make-tag-function. That function lives in pollen/tag, so we require that too:

    #lang pollen
    (require pollen/tag)
    (define my-map (make-tag-function 'map))
    my-map{How I would love this to be a map.}

    '(map "How I would love this to be a map.")

    Problem solved.

    9.3.1.3 Inserting the value of a variable

    A Pollen command name usually refers to a function, but it can also refer to a variable, which is a data value. Once you define the variable, you can insert it into your source by using the ◊ notation without any other arguments:

    #lang pollen
    (define foo "bar")
    The value of foo is foo

    The value of foo is bar

    Be careful — if you include arguments, even blank ones, Pollen will treat the command name as a function. This won’t work, because a variable is not a function:

    To understand what happens here, recall the relationship between Pollen’s command modes. The text-mode command ◊foo[] becomes the Racket-mode command (foo), which after variable substitution becomes ("bar"). If you try to evaluate ("bar") — e.g., in DrRacket — you’ll get the same error.

    #lang pollen
    (define foo "bar")
    The value of foo is foo[]

    application: not a procedure;
    +    "Fancy Sauce, $1"

    Whereas in this version, strong accepts an argument called text, but then ignores it:

    #lang pollen
    (define (strong text) '(fib "1 1 2 3 5 8 13 ..."))
    strong{Fancy Sauce, $1}

    '(fib "1 1 2 3 5 8 13 ...")

    You can attach any behavior to a command name. As your project evolves, you can also update the behavior of a command name. In that way, Pollen commands become a set of hooks to which you can attach more elaborate processing.

    Using Racket functions

    You aren’t limited to functions you define. Any function from Racket, or any Racket library, can be invoked directly by using it as a command name. Here’s the function range, which creates a list of numbers:

    #lang pollen
    range[1 20]

    '(range 1 20)

    Hold on — that’s not what we want. Where’s the list of numbers? The problem here is that we didn’t explicitly import the racket/list library, which contains the definition for range. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without racket/list, Pollen just thinks we’re trying to use range as a tag function (and if we had been, then '(range 1 20) would’ve been the right result).

    We fix this by using the require command to bring in the racket/list library, which contains the range we want:

    #lang pollen
    (require racket/list)
    range[1 20]

    '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

    Of course, you can also invoke Racket functions indirectly, by attaching them to functions you define for command names:

    #lang pollen
    (require racket/list)
    (define (rick start finish) (range start finish))
    rick[1 20]

    '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

    Let’s return to the problem that surfaced in the last section — the fact that some command names can’t be used as tag functions because they’re already being used for other things. You can work around this by defining your own tag function with a non-conflicting name.

    For instance, suppose we want to use map as a tag even though Racket is using it for its own function called map. First, we invent a command name that doesn’t conflict. Let’s call it my-map. As you learned above, Pollen will treat a new command name as a tag function by default:

    #lang pollen
    my-map{How I would love this to be a map.}

    '(my-map "How I would love this to be a map.")

    But my-map is not the tag we want. We need to define my-map to be a tag function for map. We can do this with the Pollen helper make-default-tag-function. That function lives in pollen/tag, so we require that too:

    #lang pollen
    (require pollen/tag)
    (define my-map (make-default-tag-function 'map))
    my-map{How I would love this to be a map.}

    '(map "How I would love this to be a map.")

    Problem solved.

    10.3.1.3 Inserting the value of a variable

    A Pollen command name usually refers to a function, but it can also refer to a variable, which is a data value. Once you define the variable, you can insert it into your source by using the ◊ notation without any other arguments:

    #lang pollen
    (define foo "bar")
    The value of foo is foo

    The value of foo is bar

    Be careful — if you include arguments, even blank ones, Pollen will treat the command name as a function. This won’t work, because a variable is not a function:

    To understand what happens here, recall the relationship between Pollen’s command modes. The text-mode command ◊foo[] becomes the Racket-mode command (foo), which after variable substitution becomes ("bar"). If you try to evaluate ("bar") — e.g., in DrRacket — you’ll get the same error.

    #lang pollen
    (define foo "bar")
    The value of foo is foo[]

    application: not a procedure;
    expected a procedure that can be applied to arguments
      given: "bar"
    -  arguments...: [none]

    The reason we can simply drop ◊foo into the text argument of another Pollen command is that the variable foo holds a string (i.e., a text value). When appropriate, Pollen will convert a variable to a string in a sensible way. For instance, numbers are easily converted:

    #lang pollen
    (define zam 42)
    The value of zam is zam

    The value of zam is 42

    If the variable holds a container datatype (like a list, hash, or vector), Pollen will produce the Racket text representation of the item. Here, zam is a list of integers:

    #lang pollen
    (define zam (list 1 2 3))
    The value of zam is zam

    The value of zam is '(1 2 3)

    This feature is included for your convenience as an author. But in general, your readers won’t want to see the Racket representation of a container. So in these cases, you should convert to a string manually in some sensible way. Here, the integers in the list are converted to strings, which are then combined using string-join from the racket/string library:

    #lang pollen
    (require racket/string)
    (define zam (list 1 2 3))
    The value of zam is string-join[(map number->string zam)]{ and }

    The value of zam is 1 and 2 and 3

    Pollen will still produce an error if you try to convert an esoteric value to a string. Here, zam is the addition function (+):

    #lang pollen
    (define zam +)
    The value of zam is zam

    Pollen decoder: can’t convert #<procedure:+> to string

    One special case to know about. In the examples above, there’s a word space between the variable and the other text. But suppose you need to insert a variable into text so that there’s no space in between. The simple ◊ notation won’t work, because it won’t be clear where the variable name ends and the text begins.

    For instance, suppose we want to use a variable edge next to the string px:

    #lang pollen
    (define edge 100)
    p { margin-left: edgepx; }

    Pollen decoder: can’t convert #<procedure:...t/pollen/tag.rkt:6:2> to string

    The example fails because Pollen reads the whole string after the as the single variable name edgepx. Since edgepx isn’t defined, it’s treated as a tag function, and since Pollen can’t convert a function to a string, we get an error.

    In these situations, surround the variable name with vertical bars ◊|like so| to explicitly indicate where the variable name ends. The bars are not treated as part of the name, nor are they included in the result. Once we do that, we get what we intended:

    #lang pollen
    (define edge 100)
    p { margin-left: ◊|edge|px; }

    p { margin-left: 100px; }

    If you use this notation when you don’t need to, nothing bad will happen. The vertical bars are always ignored.

    #lang pollen
    (define edge 100)
    The value of edge is ◊|edge| pixels}

    The value of edge is 100 pixels

    9.3.1.4 Inserting a comment

    Two options.

    To comment out the rest of a single line, use a lozenge followed by a semicolon ◊;.

    #lang pollen
    span{This is not a comment}
    span{Nor is this} ;span{But this is}

    '(span "This is not a comment")
    -'(span "Nor is this")

    To comment out a multiline block, use the lozenge–semicolon signal ◊; with curly braces, ◊;{like so}.

    #lang pollen
    ;{
    ◊span{This is not a comment}
    ◊span{Nor is this} ;span{But this is}
    }
    Actually, it's all a comment now

    Actually, it's all a comment now

    9.3.2 The Racket arguments

    The middle part of a text-mode Pollen command contains the Racket arguments [between square brackets.] Most often, you’ll see these used to pass extra information to commands that operate on text.

    For instance, tag functions. Recall from before that any not-yet-defined command name in Pollen is treated as a tag function:

    #lang pollen
    title{The Beginning of the End}

    '(title "The Beginning of the End")

    But what if you wanted to add attributes to this tag, so that it comes out like this?

    '(title ((class "red")(id "first")) "The Beginning of the End")

    You can do it with Racket arguments.

    Here’s the hard way. You can type out your list of attributes in Racket format and drop them into the brackets as a single argument:

    #lang pollen
    title['((class "red")(id "first"))]{The Beginning of the End}

    '(title ((class "red") (id "first")) "The Beginning of the End")

    But that’s a lot of parentheses to think about. So here’s the easy way. Anytime you use a tag function, there’s a shortcut for inserting attributes. You can enter them as a series of symbol / string pairs between the Racket-argument brackets. The only caveat is that the symbols have to begin with a quote mark ' and end with a colon :. So taken together, they look like this:

    #lang pollen
    title['class: "red" 'id: "first"]{The Beginning of the End}

    '(title ((class "red") (id "first")) "The Beginning of the End")

    Racket arguments can be any valid Racket expressions. For instance, this will also work:

    #lang pollen
    title['class: (format "~a" (* 6 7)) 'id: "first"]{The Beginning of the End}

    '(title ((class "42") (id "first")) "The Beginning of the End")

    Since Pollen commands are really just Racket arguments underneath, you can use those too. Here, we’ll define a variable called name and use it in the Racket arguments of title:

    #lang pollen
    (define name "Brennan")
    title['class: "red" 'id: name]{The Beginning of the End}

    '(title ((class "read") (id "Brennan")) "The Beginning of the End")

    You can also use this area for keyword arguments. Keyword arguments can be used to provide options for a particular Pollen command, to avoid redundancy. Suppose that instead of using the h1 ... h6 tags, you want to consolidate them into one command called heading and select the level separately. You can do this with a keyword, in this case #:level, which is passed as a Racket argument:

    #lang pollen
    (define (heading #:level which text)
       `(,(string->symbol (format "h~a" which)) ,text))
     
    heading[#:level 1]{Major league}
    heading[#:level 2]{Minor league}
    heading[#:level 6]{Trivial league}

    '(h1 "Major league")
    +  arguments...: [none]

    The reason we can simply drop ◊foo into the text argument of another Pollen command is that the variable foo holds a string (i.e., a text value). When appropriate, Pollen will convert a variable to a string in a sensible way. For instance, numbers are easily converted:

    #lang pollen
    (define zam 42)
    The value of zam is zam

    The value of zam is 42

    If the variable holds a container datatype (like a list, hash, or vector), Pollen will produce the Racket text representation of the item. Here, zam is a list of integers:

    #lang pollen
    (define zam (list 1 2 3))
    The value of zam is zam

    The value of zam is '(1 2 3)

    This feature is included for your convenience as an author. But in general, your readers won’t want to see the Racket representation of a container. So in these cases, you should convert to a string manually in some sensible way. Here, the integers in the list are converted to strings, which are then combined using string-join from the racket/string library:

    #lang pollen
    (require racket/string)
    (define zam (list 1 2 3))
    The value of zam is string-join[(map number->string zam)]{ and }

    The value of zam is 1 and 2 and 3

    Pollen will still produce an error if you try to convert an esoteric value to a string. Here, zam is the addition function (+):

    #lang pollen
    (define zam +)
    The value of zam is zam

    Pollen decoder: can’t convert #<procedure:+> to string

    One special case to know about. In the examples above, there’s a word space between the variable and the other text. But suppose you need to insert a variable into text so that there’s no space in between. The simple ◊ notation won’t work, because it won’t be clear where the variable name ends and the text begins.

    For instance, suppose we want to use a variable edge next to the string px:

    #lang pollen
    (define edge 100)
    p { margin-left: edgepx; }

    Pollen decoder: can’t convert #<procedure:...t/pollen/tag.rkt:6:2> to string

    The example fails because Pollen reads the whole string after the as the single variable name edgepx. Since edgepx isn’t defined, it’s treated as a tag function, and since Pollen can’t convert a function to a string, we get an error.

    In these situations, surround the variable name with vertical bars ◊|like so| to explicitly indicate where the variable name ends. The bars are not treated as part of the name, nor are they included in the result. Once we do that, we get what we intended:

    #lang pollen
    (define edge 100)
    p { margin-left: ◊|edge|px; }

    p { margin-left: 100px; }

    If you use this notation when you don’t need to, nothing bad will happen. The vertical bars are always ignored.

    #lang pollen
    (define edge 100)
    The value of edge is ◊|edge| pixels}

    The value of edge is 100 pixels

    10.3.1.4 Inserting a comment

    Two options.

    To comment out the rest of a single line, use a lozenge followed by a semicolon ◊;.

    #lang pollen
    span{This is not a comment}
    span{Nor is this} ;span{But this is}

    '(span "This is not a comment")
    +'(span "Nor is this")

    To comment out a multiline block, use the lozenge–semicolon signal ◊; with curly braces, ◊;{like so}.

    #lang pollen
    ;{
    ◊span{This is not a comment}
    ◊span{Nor is this} ;span{But this is}
    }
    Actually, it's all a comment now

    Actually, it's all a comment now

    10.3.2 The Racket arguments

    The middle part of a text-mode Pollen command contains the Racket arguments [between square brackets.] Most often, you’ll see these used to pass extra information to commands that operate on text.

    For instance, tag functions. Recall from before that any not-yet-defined command name in Pollen is treated as a tag function:

    #lang pollen
    title{The Beginning of the End}

    '(title "The Beginning of the End")

    But what if you wanted to add attributes to this tag, so that it comes out like this?

    '(title ((class "red")(id "first")) "The Beginning of the End")

    You can do it with Racket arguments.

    Here’s the hard way. You can type out your list of attributes in Racket format and drop them into the brackets as a single argument:

    #lang pollen
    title['((class "red")(id "first"))]{The Beginning of the End}

    '(title ((class "red") (id "first")) "The Beginning of the End")

    But that’s a lot of parentheses to think about. So here’s the easy way. Anytime you use a tag function, there’s a shortcut for inserting attributes. You can enter them as a series of symbol / string pairs between the Racket-argument brackets. The only caveat is that the symbols have to begin with a quote mark ' and end with a colon :. So taken together, they look like this:

    #lang pollen
    title['class: "red" 'id: "first"]{The Beginning of the End}

    '(title ((class "red") (id "first")) "The Beginning of the End")

    Racket arguments can be any valid Racket expressions. For instance, this will also work:

    #lang pollen
    title['class: (format "~a" (* 6 7)) 'id: "first"]{The Beginning of the End}

    '(title ((class "42") (id "first")) "The Beginning of the End")

    Since Pollen commands are really just Racket arguments underneath, you can use those too. Here, we’ll define a variable called name and use it in the Racket arguments of title:

    #lang pollen
    (define name "Brennan")
    title['class: "red" 'id: name]{The Beginning of the End}

    '(title ((class "read") (id "Brennan")) "The Beginning of the End")

    You can also use this area for keyword arguments. Keyword arguments can be used to provide options for a particular Pollen command, to avoid redundancy. Suppose that instead of using the h1 ... h6 tags, you want to consolidate them into one command called heading and select the level separately. You can do this with a keyword, in this case #:level, which is passed as a Racket argument:

    #lang pollen
    (define (heading #:level which text)
       `(,(string->symbol (format "h~a" which)) ,text))
     
    heading[#:level 1]{Major league}
    heading[#:level 2]{Minor league}
    heading[#:level 6]{Trivial league}

    '(h1 "Major league")
    '(h2 "Minor league")
    -'(h6 "Trivial league")

    9.3.3 The text argument

    The third part of a text-mode Pollen command is the text argument. The text argument {appears between curly braces}. It can contain any text you want. The text argument can also contain other Pollen commands with their own text arguments. And they can contain other Pollen commands ... and so on, all the way down.

    #lang pollen
    div{Do it again. div{And again. div{And yet again.}}}

    '(div "Do it again. " (div "And again. " (div "And yet again.")))

    Three small details to know about the text argument.

    First, the only character that needs special handling in a text argument is the lozenge . A lozenge ordinarily marks a new command. So if you want an actual lozenge to appear in the text, you have to escape it by typing ◊"◊".

    #lang pollen
    definition{This is the lozenge: "◊"}

    '(definition "This is the lozenge: ◊")

    Second, the whitespace-trimming policy. Here’s the short version: if there’s a carriage return at either end of the text argument, it is trimmed, and whitespace at the end of each line is selectively trimmed in an intelligent way. So this text argument, with carriage returns on the ends:

    #lang pollen
    div{
    Roomy!
     
    I agree.
    }

    '(div "Roomy!" "\n" "\n" "I agree.")

    Yields the same result as this one:

    #lang pollen
    div{Roomy!
     
    I agree.}

    '(div "Roomy!" "\n" "\n" "I agree.")

    For the long version, please see [future link: Spaces, Newlines, and Indentation].

    Third, within a multiline text argument, newline characters become individual strings that are not merged with adjacent text. So what you end up with is a list of strings, not a single string. That’s why in the last example, we got this:

    '(div "Roomy!" "\n" "\n" "I agree.")

    Instead of this:

    '(div "Roomy!\n\nI agree.")

    Under most circumstances, these two tagged X-expressions will behave the same way. The biggest exception is with functions. A function that operates on multiline text arguments needs to be able to handle an indefinite number of strings. For instance, this jejune function only accepts a single argument. It will work with a single-line text argument, because that produces a single string:

    #lang pollen
    (define (jejune text)
       `(jejune ,text))
    jejune{Irrational confidence}

    '(jejune "Irrational confidence")

    But watch what happens with a multiline text argument:

    #lang pollen
    (define (jejune text)
       `(jejune ,text))
    jejune{Deeply
            chastened}

    jejune: arity mismatch;
    +'(h6 "Trivial league")

    10.3.3 The text argument

    The third part of a text-mode Pollen command is the text argument. The text argument {appears between curly braces}. It can contain any text you want. The text argument can also contain other Pollen commands with their own text arguments. And they can contain other Pollen commands ... and so on, all the way down.

    #lang pollen
    div{Do it again. div{And again. div{And yet again.}}}

    '(div "Do it again. " (div "And again. " (div "And yet again.")))

    Three small details to know about the text argument.

    First, the only character that needs special handling in a text argument is the lozenge . A lozenge ordinarily marks a new command. So if you want an actual lozenge to appear in the text, you have to escape it by typing ◊"◊".

    #lang pollen
    definition{This is the lozenge: "◊"}

    '(definition "This is the lozenge: ◊")

    Second, the whitespace-trimming policy. Here’s the short version: if there’s a carriage return at either end of the text argument, it is trimmed, and whitespace at the end of each line is selectively trimmed in an intelligent way. So this text argument, with carriage returns on the ends:

    #lang pollen
    div{
    Roomy!
     
    I agree.
    }

    '(div "Roomy!" "\n" "\n" "I agree.")

    Yields the same result as this one:

    #lang pollen
    div{Roomy!
     
    I agree.}

    '(div "Roomy!" "\n" "\n" "I agree.")

    For the long version, please see [future link: Spaces, Newlines, and Indentation].

    Third, within a multiline text argument, newline characters become individual strings that are not merged with adjacent text. So what you end up with is a list of strings, not a single string. That’s why in the last example, we got this:

    '(div "Roomy!" "\n" "\n" "I agree.")

    Instead of this:

    '(div "Roomy!\n\nI agree.")

    Under most circumstances, these two tagged X-expressions will behave the same way. The biggest exception is with functions. A function that operates on multiline text arguments needs to be able to handle an indefinite number of strings. For instance, this jejune function only accepts a single argument. It will work with a single-line text argument, because that produces a single string:

    #lang pollen
    (define (jejune text)
       `(jejune ,text))
    jejune{Irrational confidence}

    '(jejune "Irrational confidence")

    But watch what happens with a multiline text argument:

    #lang pollen
    (define (jejune text)
       `(jejune ,text))
    jejune{Deeply
            chastened}

    jejune: arity mismatch;
    the expected number of arguments does not match the given number
      expected: 1
      given: 3
      arguments...:
       "Deeply"
       "\n"
    -   "chastened"

    The answer is to use a rest argument in the function, which takes the “rest” of the arguments — however many there may be — and combines them into a single list. If we rewrite jejune with a rest argument, we can fix the problem:

    #lang pollen
    (define (jejune . texts)
       `(jejune ,@texts))
    jejune{Deeply
            chastened}

    '(jejune "Deeply" "\n" "chastened")

    9.4 Further reading

    The Pollen language is a variant of Racket’s own text-processing language, called Scribble. So many things that are true about Scribble are also true about Pollen. For the sake of clarity & brevity, I’ve omitted them from this summary. But if you want the full story, see @ Syntax in the Scribble documentation.

     
    \ No newline at end of file +   "chastened"

    The answer is to use a rest argument in the function, which takes the “rest” of the arguments — however many there may be — and combines them into a single list. If we rewrite jejune with a rest argument, we can fix the problem:

    #lang pollen
    (define (jejune . texts)
       `(jejune ,@texts))
    jejune{Deeply
            chastened}

    '(jejune "Deeply" "\n" "chastened")

    10.4 Further reading

    The Pollen language is a variant of Racket’s own text-processing language, called Scribble. So many things that are true about Scribble are also true about Pollen. For the sake of clarity & brevity, I’ve omitted them from this summary. But if you want the full story, see @ Syntax in the Scribble documentation.

     
    \ No newline at end of file diff --git a/doc/second-tutorial.html b/doc/second-tutorial.html index e87dc7a..74e60e6 100644 --- a/doc/second-tutorial.html +++ b/doc/second-tutorial.html @@ -1,10 +1,5 @@ -6 Second tutorial
    6.1.0.5

    6 Second tutorial

    In this tutorial, you’ll use Pollen to publish a multiple-page article written in Markdown. You’ll learn about:

    • Using Markdown files with the preprocessor

    • X-expressions

    • Markdown authoring mode

    • Templates

    • Pagetrees

    If you want the shortest possible introduction to Pollen, try the Quick tour.

    6.1 Prerequisites

    I’ll assume you’ve completed the First tutorial and you understand how to create source files in DrRacket and view them in the project server. I will not be spelling out those tasks as I did before.

    6.2 Prelude: my principled objection to Markdown

    I recognize that people like Markdown. I want people to like Pollen too, so that’s why Markdown support exists in Pollen. But just to be clear about my own views —

    I’m mystified by the popularity of Markdown among writers. I can agree that it’s a clever and readable way of notating basic HTML. And sure, that makes it great for things like web comments, where speed and simplicity are primary virtues.

    In longer-form writing, however, its shortcomings become evident. Like programming languages, the best writing tools maximize expressive possibilities, and minimize constraints. But Markdown is hugely constrained. First and worst, Markdown isn’t semantic. It only knows about formatting, and in that regard, isn’t that much of an improvement on tools like Microsoft Word. Second, even as a formatting-notation tool, it’s limited to a small subset of the already-small set of formatting tags permitted in HTML. Third, it can’t be extended by an author.

    An animating principle of Pollen, as explained in the Backstory, is that after 20 years, we ought to move beyond thinking of HTML as a source format. Since Markdown is just well-disguised HTML, a vote for Markdown is really a vote to continue the status quo (albeit with fewer angle brackets). For me, that’s not good enough. I’m ready for the tools to expand to fit my ideas; I don’t want to keep cutting down my ideas to fit the tools.

    All that said, if you genuinely prefer Markdown, I’m not looking to pry it from your fingers. Pollen has excellent Markdown support (due entirely to Greg Hendershott’s excellent Markdown parser for Racket). It makes Markdown more useful.

    But let’s make a deal, Markdown fans. Having met you more than halfway, will you at least consider that Pollen markup might be a better option for you than Markdown? Because it can notate anything that’s in your brain, not just a subset of HTML? And if The book is a program, the source for that book should look more like your brain, and less like HTML?

    That’s all I ask.

    6.3 Markdown in Pollen: two options

    There are two ways to use Markdown within Pollen: you can either send Markdown files through the preprocessor, or use Markdown authoring mode.

    The preprocessor approach is better if you want to end up with a set of Markdown files that can be passed along to a HTML converter (or other Markdown-to-______ converter) elsewhere.

    The authoring-mode approach is better if you want to end up with something other than Markdown, e.g., finished HTML files.

    6.3.1 Using Markdown with the preprocessor

    Because Markdown is a text-based format, you can use the Pollen preprocessor to add programmatic features to existing Markdown files. (See Working with the preprocessor in the First tutorial if you need a refresher.)

    Suppose we have a Markdown file called brennan.md that we want to use with the preprocessor. Create this file in DrRacket, save it, and start the project server in that directory.

    "brennan.md"

    My name is _Brennan_, and I enjoy:

     

    + boring sauce

     

    + 24 fish nuggets

    You’ll be able to see this file in the project server, but for now, it’s just a static file. Pollen isn’t doing anything to it.

    Let’s change that. Consistent with the usual preprocessor practice, we add #lang pollen as the first line, and append the .pp file extension, so our new preprocessor-ready file looks like this:

    "brennan.md.pp"

    #lang pollen

     

    My name is _Brennan_, and I enjoy:

     

    + boring sauce

     

    + 24 fish nuggets

    Go back to the project server and you’ll see the new filename. When you click on it, Pollen will render a new markdown.md file, but it will look the same as the one you had before.

    Now we’ll change some of the values using Pollen commands:

    "brennan.md.pp"

    #lang pollen

     

    ◊(define sauce-type "fancy")

    ◊(define nugget-type "chicken")

    ◊(define nugget-quantity (* 2 2 3))

     

    My name is _Brennan_, and I enjoy:

     

    + ◊sauce-type sauce

     

    + ◊nugget-quantity ◊nugget-type nuggets

    When you reload this file in the project server, brennan.md will be regenerated, and will now look like this:

    My name is _Brennan_, and I enjoy:

     

    + fancy sauce

     

    + 12 chicken nuggets

    Instead of running Markdown files through the preprocessor, you can also use Markdown authoring mode within Pollen. This is the better choice if you want to end up with rendered HTML files.

    But first, let’s pause to clarify the general concept of an authoring mode.

    6.3.2 Authoring mode

    Though the preprocessor is useful, it limits you to inserting chunks of text at various positions into an existing file.

    Pollen’s authoring mode, by contrast, parses the whole source file into a special data structure called an X-expression. You can then process the whole X-expression any way you like, and output to any format you like — or multiple formats — using a template.

    Compared to the preprocessor, authoring mode offers more abstraction and flexibility. Of course, it’s also a bit more effort to set up.

    Pollen offers two variants of authoring mode: one that uses Markdown syntax (which we’ll cover later in this tutorial) and the other that uses a free-form markup syntax (which we’ll cover in the next tutorial). In both cases, the basic idea is the same: parse the source into an X-expression, and then output it using a template.

    6.3.3 X-expressions

    Don’t skip this part! It’s not the same old shit. And it describes a concept that’s key to how Pollen works.

    I avoid nerdy jargon whenever possible. But in this case, the thing is called an X-expression throughout the Racket documentation, for good reasons. So I use the term too. Better to acclimate you now.

    An X-expression is a way of representing markup-based data in code. X-expressions are indigenous to Lisp-based languages like Pollen and Racket. They don’t exist in Python or JavaScript or Ruby.

    Let’s start with the part you’re familiar with. By “markup-based data,” I mean things like HTML and XML and SVG. The idea is that you have text-based data surrounded by tags. Each tag can also have its own attributes that are made of keys and values. Tags can contain other tags, thus creating a tree-like structure. Right? You know what I mean:

    <body><h1>Hello world</h1><p class="first">Nice to <i>see</i> you.</p></body>

    An X-expression is just a simplified, generalized method of notation for these data structures — much like Markdown is a simplified method of notation for HTML. To see the relationship, we’ll convert one into the other.

    First, we change the angle brackets to parentheses, and only use them on the outside of tags:

    (body (h1 Hello world /h1) (p class="first" Nice to (i see /i) you. /p) /body)

    Then we get rid of the closing tags, which are superfluous, since the closing parenthesis marks the end of the tag perfectly well:

    (body (h1 Hello world) (p class="first" Nice to (i see) you.))

    However, this creates ambiguity between the name of the tag and the content. So we’ll put the content within quote marks:

    (body (h1 "Hello world") (p class="first" "Nice to" (i "see") "you."))

    As for the class attribute, we need to distinguish it from both the markup tags and the content, so we’ll move it between double parentheses:

    (body (h1 "Hello world") (p ((class "first")) "Nice to" (i "see") "you."))

    Net of a few boring details, that’s basically all there is to it.

    So why is it called an X-expression? Lisp languages are built out of units called S-expressions, which look like this:

    (and (txexpr? x) (member (get-tag x) (project-block-tags)) #t))

    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 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 X is short for XML-like).

    For handling markup-based data, X-expressions have some useful advantages compared to other methods:

    • Readability. X-expressions retain all the semantics of markup-based data while dispensing with the infamous verbosity.

    • A hybrid between a tree and a string. Most programming languages represent markup-based data either as a string or as an XML tree. Neither is a good choice. The string captures none of the internal structure of the data. An XML tree captures the structure, but conceals the sequential nature of the data elements. The X-expression gets both.

    • An ideal match for an expression-based programming language. Aside from some notational details, X-expressions are just a subset of S-expressions generally, which are the building block of Racket. Processing X-expressions in Racket maximizes flexibility and minimizes yak-shaving.

    Given the close kinship between XML-ish data structures and Lisp-ish programming languages, I have no explanation why, during the Internet era, they have not been paired more often.

    In Pollen’s authoring modes, your source file is parsed into an X-expression, which can then be processed further before being injected into a template & converted to output. As a first example, we’ll look at Markdown authoring mode.

    6.3.4 Markdown authoring mode

    Let’s start putting together our article. For simplicity, I’m going to use unrealistically short sample texts. But you can use whatever Markdown content you want.

    We want to use Markdown authoring mode to make a file that will ultimately be HTML. So consistent with Pollen file-naming conventions (see Saving & naming your source file), we’ll start with our desired output filename, article.html, and then append the Markdown authoring suffix, .pmd. So in DrRacket, start a new file called article.html.pmd and put some Markdown in it:

    "article.html.pmd"

    #lang pollen

     

    Deep Thought

    ============

     

    I am **so** happy to be writing this.

    Before you preview this file in the project server, click the Run button in DrRacket just to see what the file produces. You’ll see something like this:

    ’(root (h1 ((id "my-article")) "Deep Thought") (p () "I am " -
    (strong () "so") " happy to be writing this."))

    You should now be able to recognize this as an X-expression. In authoring mode, Pollen parses your Markdown into the corresponding HTML entities, but then provides the data as an X-expression rather than finished HTML.

    The empty parentheses () after p and strong signal that the tag’s attributes are empty. When you write an X-expression without attributes, these parentheses are optional — (tag () "text") and (tag "text") are equivalent — but Pollen will always print X-expressions this way.

    From what you learned in the last section, it should be evident that this X-expression corresponds to HTML that looks like this:

    <root><h1 id="my-article">Deep Thought</h1><p>I am
    <strong>so</strong> happy to be writing this.</p></root>

    “But what’s this root tag? That’s not HTML.” An X-expression must have a root tag, so in the spirit of obviousness, every X-expression produced by a source file in authoring mode will start with root. If you don’t need it, you can discard it. But it also creates a useful hook for further processing, as we’ll see later.

    By the way, as review, let’s remind ourselves how this is different from preprocessor mode. Let’s take the same Markdown content, but this time put it into a preprocessor source file called article.md.pp.

    "article.md.pp"

    #lang pollen

     

    Deep Thought

    ============

     

    I am **so** happy to be writing this.

    When you run this file in DrRacket, you’ll see:

    Deep Thought
    ============

    I am **so** happy to be writing this.

    Hopefully, this result makes sense to you: when you run Markdown source in preprocessor mode, you get Markdown. When you run Markdown source in authoring mode, you get an X-expression.

    6.4 Templates

    So how do you convert an X-expression into a finished file? You use a Pollen template, which takes data from an X-expression and converts it to the target format.

    If you’ve used other web-publishing systems, templates are probably a familiar idea. Templates in Pollen are in some ways similar to the ones you’ve seen before, but in other ways different.

    First, the similarities. At its simplest, a template holds boilerplate material that you want to reuse across multiple pages. For instance, in a set of HTML pages, you might have layout and navigation elements that stay the same, while the content changes. In that case, you could put the layout and navigation in the template, and keep the content in your Pollen source files. When you want to add a new page, you can make a new source file and just use it with the existing template. Moreover, if you want to change the layout and navigation globally, you can just change the template, rather than changing the source files.

    Pollen templates, like others, can also have conditional features — meaning, you can embed simple code in your templates that allows them to change based on the content in the page. For instance, a template could show or hide “previous page” and “next page” links depending on whether there’s actually a previous or next page.

    The major difference with Pollen templates is that there’s no special “template language” you need to learn, with magic delimiters and whatnot. Instead, you can use all the same Pollen commands in a template that you can in authoring mode or preprocessor mode.

    To see a template in action, let’s return to the source file we started in the last section:

    "article.html.pmd"

    #lang pollen

     

    Deep Thought

    ============

     

    I am **so** happy to be writing this.

    Last time, I had you run this file in DrRacket to see the X-expression it produced. This time, load it in the project server. You’ll see something like this:

    Deep Thought

    I am so happy to be writing this.

    Here, you’re seeing the X-expression from your source combined with an HTML template, which adds the necessary boilerplate for the finished HTML:

    <html><head><meta charset="UTF-8" /></head><body> -
    <root><h1 id="my-article">Deep Thought</h1><p>I am -
    <strong>so</strong> happy to be writing this.</p></root> -
    </body></html>

    But wait — where did the template come from? When you view an authoring-mode source file in the project server without specifying a template, Pollen helps you out and uses its fallback template. The fallback template is just a minimal template that’s used as a last resort. Under ordinary circumstances, seeing the fallback template usually signals a problem (e.g., Pollen couldn’t find the template you asked for).

    But we can learn a few things from the fallback template about how to make an HTML template.

    6.4.1 The ->html function and the doc variable

    This is the fallback template that Pollen uses.

    "fallback.html"

    ◊(->html (html (head (meta 'charset: "UTF-8")) (body doc)))

    It has three key ingredients.

    First, there’s an X-expression that represents a basic HTML page:

    (html (head (meta 'charset: "UTF-8")) (body))

    This is equivalent to the HTML:

    <html><head><meta charset="UTF-8"></head><body></body></html>

    But within a template, we need to explicitly convert from X-expression to HTML. So we wrap this X-expression with our second key ingredient, the Pollen command ->html:

    ◊(->html (html (head (meta 'charset: "UTF-8")) (body)))

    Third, we need to include the content from our source file. We do this by putting the variable doc inside the body tag.

    ◊(->html (html (head (meta 'charset: "UTF-8")) (body doc)))

    By convention, every Pollen source file makes its output available through the variable doc. A source file in preprocessor mode puts its text result in doc. And a source file in authoring mode puts its X-expression result in doc.

    You can change the name to something other than doc by changing world:main-pollen-export.

    Under the hood, a template is just a partial program that relies on a set of variables defined by another source file (fancy name: lexical context). So if you ran this template on its own, nothing would happen, because doc isn’t defined. But when you run it in the context of another source file, it picks up the doc that’s defined by that file.

    Caution — despite the name, a Pollen template is not necessarily a file of the type suggested by its extension. For instance, fallback.html is a file that ultimately produces HTML, but it’s not actually written in HTML.

    It could be, however. Here’s an equivalent way of writing fallback.html that inserts doc into actual HTML, rather than making the whole thing an X-expression.

    <html><head><meta charset="UTF-8"></head>

    <body>◊(->html doc)</body></html>

    Notice that we still need to use the ->html function, but this time, instead of surrounding a larger X-expression, it just goes around doc.

    Truly, there is no difference between these two methods. Use whichever works best for you. I often prefer the second method because I like to build & test HTML layouts by hand using placeholder content to make sure all the fiddly bits work. Then it’s easy to replace the placeholder content with (->html doc), and it becomes a template.

    6.4.2 Making a custom template

    We’ll use these three ingredients to make our own template for article.html.pmd.

    In general, template files can have any name you want. But by default, Pollen will first look for a file in your project directory called template.ext, where ext matches the output-file extension of the source file. So if your source file is database.xml.pmd, Pollen will look for template.xml. And for article.html.pmd, Pollen will look for template.html.

    Therefore, to set up a custom template, all we need to do is create a file called template.html in our project directory, and make sure it has the three key ingredients we saw in the fallback template. Pollen will automatically apply it to article.html.pmd when we view it in the project server.

    But don’t take my word for it. In your project directory, create a new file called template.html:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>Custom template</title></head>

    <body>◊(->html doc)</body>

    </html>

    Recall from the last section that this is the same as the fallback template, but written out in HTML, and with a title element added. In fact, you can now refresh article.html in the project server. Does it look different? No — it won’t, because the resulting template is the same. You should notice, however, that the title of the browser window is now “Custom template,” because Pollen is relying on your new template file, rather than the fallback template.

    Let’s change our custom template by adding a style block:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>Custom template</title>

    <style type="text/css">

    body {padding: 3em; font-size: 20px;}

    h1 {background: gray; color: white;}

    strong {color: red;}

    </style></head>

    <body>◊(->html doc)</body>

    </html>

    When you refresh article.html in the project server, you’ll see that the heading now has a gray background, and one word in the text is red.

    Feel free to add other settings to template.html, or update the text in article.html, and see how the page changes. As you’d expect, the project server keeps an eye on both your source files and your template files, and if one changes, it will refresh the output file automatically.

    6.4.3 Inserting specific source data into templates

    In the last example, we used doc to insert the entire content of the source file — as an X-expression — into the template.

    But what if you want to only insert part of your source file into the template? For instance, you’ll look like a dork if the title on each page is “Custom template.” So let’s fix that.

    When you’re working in a template, Pollen provides a select function that lets you extract the content of a specific tag, like so: ◊(select tag-name doc), which means “get the content of tag-name out of doc and put it here.”

    Let’s suppose that we’d rather use the name of the article — Deep Thought — as the page title. We’re going to put a ◊(select ...) command inside the <title> tag.

    Beyond that, we just need to know the tag name that contains the title. If we have a little Markdown expertise, we might already know that this part of our Markdown source:

    Deep Thought

    ============

    is going to produce a tag named h1.

    What if we don’t have all the Markdown conversions memorized? No problem. We can still figure out the tag name by running the article.html.pmd source file in DrRacket and looking at the X-expression that results:

    ’(root (h1 ((id "my-article")) "Deep Thought") (p () "I am " -
    (strong () "so") " happy to be writing this."))

    Either way, now we know that the text Deep Thought lives in the h1 tag. So we update our template accordingly (for brevity, I’m going to omit the style tag in these examples, but it’s fine to leave it in):

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊(select 'h1 doc)</title></head>

    <body>◊(->html doc)</body>

    </html>

    When you refresh the page in the project server, the page title will now appear as “Deep Thought.” Of course, you can also combine static and dynamic elements in your template, like so:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊(select 'h1 doc), by MB</title></head>

    <body>◊(->html doc)</body>

    </html>

    The page title will now be “Deep Thought, by MB”.

    A couple notes on command syntax. We inserted the select and ->html commands using Racket-mode syntax. We could also use text-mode syntax and write the commands this way:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊select['h1 doc], by MB</title></head>

    <body>◊->html[doc]</body>

    </html>

    This is exactly equivalent to the previous example. Skeptics are welcome to confirm this by checking the result in the project server.

    Finally, notice that in the select command, the tag name 'h1 is written with a quote mark, whereas doc is not. This is an easy place to get tripped up, but the rule is simple: you don’t use a quote mark when you’re referring to the name of an existing function or variable (like select or doc). But you do need a quote mark when you’re using the text as a literal value.

    Racket (and hence Pollen) makes a distinction between Symbols (e.g. 'h1) and Strings (e.g. "h1"). Without getting into the weeds, just note for now that the tag of an X-expression is always a symbol, not a string. But if you write ◊(select "h1" doc), the command will still work, because Pollen will treat it as ◊(select 'h1 doc), consistent with a general policy of not being persnickety about input types when the intention is clear.

    6.4.4 Linking to an external CSS file

    If you’re a super web hotshot, you probably don’t put your CSS selectors in the <head> tag. Instead, you link to an external CSS file. So it will not surprise you that in Pollen, you can do this by adding the usual <link> tag to your HTML template, in this case a file called styles.css:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊select['h1 doc], by MB</title></head>

    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />

    <body>◊->html[doc]</body>

    </html>

    Fans of hand-coded CSS, I trust you to take it from here: make your styles.css file, and enjoy the results.

    But folks who paid attention during the First tutorial might be wondering “Can we link to a dynamically generated styles.css.pp file?”

    Yes, of course. Here’s the rule of thumb: when you’re making links between files — whether CSS, or HTML, or anything else — Pollen doesn’t care whether the file is static or dynamic. You just refer to it by its ultimate name, in this case styles.css. If a static styles.css file exists, Pollen will use that. If it doesn’t, Pollen will look for a source file it can use to make styles.css, and generate it on the spot. (You can also start with a static file, and change it to be dynamic later, and Pollen will do the right thing.)

    So to use a dynamic CSS file, we don’t need to make any changes to template.html. We just need to add styles.css.pp to the project directory:

    "styles.css.pp"

    #lang pollen

     

    ◊(define h1-color "blue")

    ◊(define strong-color "green")

     

    body {padding: 3em; font-size: 20px;}

    h1 {background: ◊|h1-color|; color: white;}

    strong {color: ◊|strong-color|;}

    Now, when you refresh article.html in the project server, Pollen will generate the styles.css file it needs, and you’ll see the new colors in the page. As before, if you update styles.css.pp, Pollen will notice and regenerate the CSS file when you refresh the page.

    Can you add multiple dynamic style sheets? Yes. +6 Second tutorial

    6.1.0.5

    6 Second tutorial

    In this tutorial, you’ll use Pollen to publish a multiple-page article written in Markdown. You’ll learn about:

    • Using Markdown files with the preprocessor

    • X-expressions

    • Markdown authoring mode

    • Templates

    • Pagetrees

    If you want the shortest possible introduction to Pollen, try the Quick tour.

    6.1 Prerequisites

    I’ll assume you’ve completed the First tutorial and you understand how to create source files in DrRacket and view them in the project server. I will not be spelling out those tasks as I did before.

    6.2 Prelude: my principled objection to Markdown

    I recognize that people like Markdown. I want people to like Pollen too, so that’s why Markdown support exists in Pollen. But just to be clear about my own views —

    I’m mystified by the popularity of Markdown among writers. I can agree that it’s a clever and readable way of notating basic HTML. And sure, that makes it great for things like web comments, where speed and simplicity are primary virtues.

    In longer-form writing, however, its shortcomings become evident. Like programming languages, the best writing tools maximize expressive possibilities, and minimize constraints. But Markdown is hugely constrained. First and worst, Markdown isn’t semantic. It only knows about formatting, and in that regard, isn’t that much of an improvement on tools like Microsoft Word. Second, even as a formatting-notation tool, it’s limited to a small subset of the already-small set of formatting tags permitted in HTML. Third, it can’t be extended by an author.

    An animating principle of Pollen, as explained in the Backstory, is that after 20 years, we ought to move beyond thinking of HTML as a source format. Since Markdown is just well-disguised HTML, a vote for Markdown is really a vote to continue the status quo (albeit with fewer angle brackets). For me, that’s not good enough. I’m ready for the tools to expand to fit my ideas; I don’t want to keep cutting down my ideas to fit the tools.

    All that said, if you genuinely prefer Markdown, I’m not looking to pry it from your fingers. Pollen has excellent Markdown support (due entirely to Greg Hendershott’s excellent Markdown parser for Racket). It makes Markdown more useful.

    But let’s make a deal, Markdown fans. Having met you more than halfway, will you at least consider that Pollen markup might be a better option for you than Markdown? Because it can notate anything that’s in your brain, not just a subset of HTML? And if The book is a program, the source for that book should look more like your brain, and less like HTML?

    That’s all I ask.

    6.3 Markdown in Pollen: two options

    There are two ways to use Markdown within Pollen: you can either send Markdown files through the preprocessor, or use Markdown authoring mode.

    The preprocessor approach is better if you want to end up with a set of Markdown files that can be passed along to a HTML converter (or other Markdown-to-______ converter) elsewhere.

    The authoring-mode approach is better if you want to end up with something other than Markdown, e.g., finished HTML files.

    6.3.1 Using Markdown with the preprocessor

    Because Markdown is a text-based format, you can use the Pollen preprocessor to add programmatic features to existing Markdown files. (See Working with the preprocessor in the First tutorial if you need a refresher.)

    Suppose we have a Markdown file called brennan.md that we want to use with the preprocessor. Create this file in DrRacket, save it, and start the project server in that directory.

    "brennan.md"
    My name is _Brennan_, and I enjoy:
     
    + boring sauce
     
    + 24 fish nuggets

    You’ll be able to see this file in the project server, but for now, it’s just a static file. Pollen isn’t doing anything to it.

    Let’s change that. Consistent with the usual preprocessor practice, we add #lang pollen as the first line, and append the .pp file extension, so our new preprocessor-ready file looks like this:

    "brennan.md.pp"
    #lang pollen
     
    My name is _Brennan_, and I enjoy:
     
    + boring sauce
     
    + 24 fish nuggets

    Go back to the project server and you’ll see the new filename. When you click on it, Pollen will render a new markdown.md file, but it will look the same as the one you had before.

    Now we’ll change some of the values using Pollen commands:

    "brennan.md.pp"
    #lang pollen
     
    (define sauce-type "fancy")
    (define nugget-type "chicken")
    (define nugget-quantity (* 2 2 3))
     
    My name is _Brennan_, and I enjoy:
     
    + sauce-type sauce
     
    + nugget-quantity nugget-type nuggets

    When you reload this file in the project server, brennan.md will be regenerated, and will now look like this:

    My name is _Brennan_, and I enjoy:

     

    + fancy sauce

     

    + 12 chicken nuggets

    Instead of running Markdown files through the preprocessor, you can also use Markdown authoring mode within Pollen. This is the better choice if you want to end up with rendered HTML files.

    But first, let’s pause to clarify the general concept of an authoring mode.

    6.3.2 Authoring mode

    Though the preprocessor is useful, it limits you to inserting chunks of text at various positions into an existing file.

    Pollen’s authoring mode, by contrast, parses the whole source file into a special data structure called an X-expression. You can then process the whole X-expression any way you like, and output to any format you like — or multiple formats — using a template.

    Compared to the preprocessor, authoring mode offers more abstraction and flexibility. Of course, it’s also a bit more effort to set up.

    Pollen offers two variants of authoring mode: one that uses Markdown syntax (which we’ll cover later in this tutorial) and the other that uses a free-form markup syntax (which we’ll cover in the next tutorial). In both cases, the basic idea is the same: parse the source into an X-expression, and then output it using a template.

    6.3.3 X-expressions

    Don’t skip this section! It explains a concept that’s essential to understanding how Pollen works.

    I avoid nerdy jargon whenever possible. But in this case, the thing is called an X-expression throughout the Racket documentation, for good reasons. So I use the term too. Better to acclimate you now.

    An X-expression is a way of representing markup-based data in code. X-expressions are indigenous to Lisp-based languages like Pollen and Racket. They don’t exist in Python or JavaScript or Ruby.

    Let’s start with the part you’re familiar with. By “markup-based data,” I mean things like HTML and XML and SVG. The idea is that you have text-based data surrounded by tags. Each tag can also have its own attributes that are made of keys and values. Tags can contain other tags, thus creating a tree-like structure. Right? You know what I mean:

    <body><h1>Hello world</h1><p class="first">Nice to <i>see</i> you.</p></body>

    An X-expression is just a simplified, generalized method of notation for these data structures — much like Markdown is a simplified method of notation for HTML. To see the relationship, we’ll convert one into the other.

    First, we change the angle brackets to parentheses, and only use them on the outside of tags:

    (body (h1 Hello world /h1) (p class="first" Nice to (i see /i) you. /p) /body)

    Then we get rid of the closing tags, which are superfluous, since each closing parenthesis adequately marks the end of the tag:

    (body (h1 Hello world) (p class="first" Nice to (i see) you.))

    However, this creates ambiguity between the name of the tag and the content. So we’ll put the content within double quotes:

    (body (h1 "Hello world") (p class="first" "Nice to" (i "see") "you."))

    As for the class attribute, we need to distinguish it from both the markup tags and the content, so we’ll move it between double parentheses:

    (body (h1 "Hello world") (p ((class "first")) "Nice to" (i "see") "you."))

    Net of a few boring details, that’s basically all there is to it.

    So why is it called an X-expression? Lisp languages are built out of units called S-expressions, which look like this:

    (and (txexpr? x) (member (get-tag x) (project-block-tags)) #t))

    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 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 X is short for XML-like).

    For handling markup-based data, X-expressions have some useful advantages compared to other methods:

    • Readability. X-expressions retain all the semantics of markup-based data while dispensing with the infamous verbosity.

    • A hybrid between a tree and a string. Most programming languages represent markup-based data either as a string or as an XML tree. Neither is a good choice. The string captures none of the internal structure of the data. An XML tree captures the structure, but conceals the sequential nature of the data elements. The X-expression gets both.

    • An ideal match for an expression-based programming language. Aside from some notational details, X-expressions are just a subset of S-expressions generally, which are the building block of Racket. Processing X-expressions in Racket maximizes flexibility and minimizes yak-shaving.

    Given the close kinship between XML-ish data structures and Lisp-ish programming languages, I have no explanation why, during the Internet era, they have not been paired more often.

    In Pollen’s authoring modes, your source file is parsed into an X-expression, which can then be processed further before being injected into a template & converted to output. As a first example, we’ll look at Markdown authoring mode.

    6.3.4 Markdown authoring mode

    Let’s start putting together our article. For simplicity, I’m going to use unrealistically short sample texts. But you can use whatever Markdown content you want.

    We want to use Markdown authoring mode to make a file that will ultimately be HTML. So consistent with Pollen file-naming conventions (see Saving & naming your source file), we’ll start with our desired output filename, article.html, and then append the Markdown authoring suffix, .pmd. So in DrRacket, start a new file called article.html.pmd and put some Markdown in it:

    "article.html.pmd"
    #lang pollen
     
    Deep Thought
    ============
     
    I am **so** happy to be writing this.

    Before you preview this file in the project server, click the Run button in DrRacket just to see what the file produces. You’ll see something like this:

    '(root (h1 ((id "my-article")) "Deep Thought") (p () "I am "
    (strong () "so") " happy to be writing this."))

    You should now be able to recognize this as an X-expression. In authoring mode, Pollen parses your Markdown into the corresponding HTML entities, but then provides the data as an X-expression rather than finished HTML.

    The empty parentheses () after p and strong signal that the tag’s attributes are empty. When you write an X-expression without attributes, these parentheses are optional — (tag () "text") and (tag "text") are equivalent — but Pollen will always print X-expressions this way.

    From what you learned in the last section, it should be evident that this X-expression corresponds to HTML that looks like this:

    <root><h1 id="my-article">Deep Thought</h1><p>I am
    <strong>so</strong> happy to be writing this.</p></root>

    “But what’s this root tag? That’s not HTML.” An X-expression must have a root tag, so in the spirit of obviousness, every X-expression produced by a source file in authoring mode will start with root. If you don’t need it, you can discard it. But it also creates a useful hook for further processing, as we’ll see later.

    By the way, as review, let’s remind ourselves how this is different from preprocessor mode. Let’s take the same Markdown content, but this time put it into a preprocessor source file called article.md.pp.

    "article.md.pp"
    #lang pollen
     
    Deep Thought
    ============
     
    I am **so** happy to be writing this.

    When you run this file in DrRacket, you’ll see:

    Deep Thought
    ============

    I am **so** happy to be writing this.

    Hopefully, this result makes sense to you: when you run Markdown source in preprocessor mode, you get Markdown. When you run Markdown source in authoring mode, you get an X-expression.

    6.4 Templates

    So how do you convert an X-expression into a finished file? You use a Pollen template, which takes data from an X-expression and converts it to the target format.

    If you’ve used other web-publishing systems, templates are probably a familiar idea. Templates in Pollen are in some ways similar to the ones you’ve seen before, but in other ways different.

    First, the similarities. At its simplest, a template holds boilerplate material that you want to reuse across multiple pages. For instance, in a set of HTML pages, you might have layout and navigation elements that stay the same, while the content changes. In that case, you could put the layout and navigation in the template, and keep the content in your Pollen source files. When you want to add a new page, you can make a new source file and just use it with the existing template. Moreover, if you want to change the layout and navigation globally, you can just change the template, rather than changing the source files.

    Pollen templates, like others, can also have conditional features — meaning, you can embed simple code in your templates that allows them to change based on the content in the page. For instance, a template could show or hide “previous page” and “next page” links depending on whether there’s actually a previous or next page.

    The major difference with Pollen templates is that there’s no special “template language” you need to learn, with magic delimiters and whatnot. Instead, you can use all the same Pollen commands in a template that you can in authoring mode or preprocessor mode.

    To see a template in action, let’s return to the source file we started in the last section:

    "article.html.pmd"
    #lang pollen
     
    Deep Thought
    ============
     
    I am **so** happy to be writing this.

    Last time, I had you run this file in DrRacket to see the X-expression it produced. This time, load it in the project server. You’ll see something like this:

    Deep Thought

     

    I am so happy to be writing this.

    Here, you’re seeing the X-expression from your source combined with an HTML template, which adds the necessary boilerplate for the finished HTML:

    <html><head><meta charset="UTF-8" /></head><body>
    <root><h1 id="my-article">Deep Thought</h1><p>I am
    <strong>so</strong> happy to be writing this.</p></root>
    </body></html>

    But wait — where did the template come from? When you view an authoring-mode source file in the project server without specifying a template, Pollen helps you out and uses its fallback template. The fallback template is just a minimal template that’s used as a last resort. Under ordinary circumstances, seeing the fallback template usually signals a problem (e.g., Pollen couldn’t find the template you asked for).

    But we can learn a few things from the fallback template about how to make an HTML template.

    6.4.1 The ->html function and the doc variable

    This is the fallback template that Pollen uses.

    "fallback.html"
    (->html (html (head (meta 'charset: "UTF-8")) (body doc)))

    It has three key ingredients.

    First, there’s an X-expression that represents a basic HTML page:

    (html (head (meta 'charset: "UTF-8")) (body))

    This is equivalent to the HTML:

    <html><head><meta charset="UTF-8"></head><body></body></html>

    But within a template, we need to explicitly convert from X-expression to HTML. So we wrap this X-expression with our second key ingredient, the Pollen command ->html:

    (->html (html (head (meta 'charset: "UTF-8")) (body)))

    Third, we need to include the content from our source file. We do this by putting the variable doc inside the body tag.

    (->html (html (head (meta 'charset: "UTF-8")) (body doc)))

    By convention, every Pollen source file makes its output available through the variable doc. A source file in preprocessor mode puts its text result in doc. And a source file in authoring mode puts its X-expression result in doc.

    You can change the name to something other than doc by changing world:main-pollen-export.

    Under the hood, a template is just a partial program that relies on a set of variables defined by another source file (fancy name: lexical context). So if you ran this template on its own, nothing would happen, because doc isn’t defined. But when you run it in the context of another source file, it picks up the doc that’s defined by that file.

    Caution — despite the name, a Pollen template is not necessarily a file of the type suggested by its extension. For instance, fallback.html is a file that ultimately produces HTML, but it’s not actually written in HTML.

    It could be, however. Here’s an equivalent way of writing fallback.html that inserts doc into actual HTML, rather than making the whole thing an X-expression.

    <html><head><meta charset="UTF-8"></head>
    <body>◊(->html doc)</body></html>

    Notice that we still need to use the ->html function, but this time, instead of surrounding a larger X-expression, it just goes around doc.

    Truly, there is no difference between these two methods. Use whichever works best for you. I often prefer the second method because I like to build & test HTML layouts by hand using placeholder content to make sure all the fiddly bits work. Then it’s easy to replace the placeholder content with (->html doc), and it becomes a template.

    6.4.2 Making a custom template

    We’ll use these three ingredients to make our own template for article.html.pmd.

    In general, template files can have any name you want. But by default, Pollen will first look for a file in your project directory called template.ext, where ext matches the output-file extension of the source file. So if your source file is database.xml.pmd, Pollen will look for template.xml. And for article.html.pmd, Pollen will look for template.html.

    Therefore, to set up a custom template, all we need to do is create a file called template.html in our project directory, and make sure it has the three key ingredients we saw in the fallback template. Pollen will automatically apply it to article.html.pmd when we view it in the project server.

    But don’t take my word for it. In your project directory, create a new file called template.html:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>Custom template</title></head>
    <body>◊(->html doc)</body>
    </html>

    Recall from the last section that this is the same as the fallback template, but written out in HTML, and with a title element added. In fact, you can now refresh article.html in the project server. Does it look different? No — it won’t, because the resulting template is the same. You should notice, however, that the title of the browser window is now “Custom template,” because Pollen is relying on your new template file, rather than the fallback template.

    Let’s change our custom template by adding a style block:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>Custom template</title>
    <style type="text/css">
    body {padding: 3em; font-size: 20px;}
    h1 {background: gray; color: white;}
    strong {color: red;}
    </style></head>
    <body>◊(->html doc)</body>
    </html>

    When you refresh article.html in the project server, you’ll see that the heading now has a gray background, and one word in the text is red.

    Feel free to add other settings to template.html, or update the text in article.html, and see how the page changes. As you’d expect, the project server keeps an eye on both your source files and your template files, and if one changes, it will refresh the output file automatically.

    6.4.3 Inserting specific source data into templates

    In the last example, we used doc to insert the entire content of the source file — as an X-expression — into the template.

    But what if you want to only insert part of your source file into the template? For instance, you’ll look like a dork if the title on each page is “Custom template.” So let’s fix that.

    When you’re working in a template, Pollen provides a select function that lets you extract the content of a specific tag, like so: ◊(select tag-name doc), which means “get the content of tag-name out of doc and put it here.”

    Let’s suppose that we’d rather use the name of the article — Deep Thought — as the page title. We’re going to put a ◊(select ...) command inside the <title> tag.

    Beyond that, we just need to know the tag name that contains the title. If we have a little Markdown expertise, we might already know that this part of our Markdown source:

    Deep Thought
    ============

    is going to produce a tag named h1.

    What if we don’t have all the Markdown conversions memorized? No problem. We can still figure out the tag name by running the article.html.pmd source file in DrRacket and looking at the X-expression that results:

    '(root (h1 ((id "my-article")) "Deep Thought") (p () "I am "
    (strong () "so") " happy to be writing this."))

    Either way, now we know that the text Deep Thought lives in the h1 tag. So we update our template accordingly (for brevity, I’m going to omit the style tag in these examples, but it’s fine to leave it in):

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊(select 'h1 doc)</title></head>
    <body>◊(->html doc)</body>
    </html>

    When you refresh the page in the project server, the page title will now appear as “Deep Thought.” Of course, you can also combine static and dynamic elements in your template, like so:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊(select 'h1 doc), by MB</title></head>
    <body>◊(->html doc)</body>
    </html>

    The page title will now be “Deep Thought, by MB”.

    A couple notes on command syntax. We inserted the select and ->html commands using Racket-mode syntax. We could also use text-mode syntax and write the commands this way:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊select['h1 doc], by MB</title></head>
    <body>◊->html[doc]</body>
    </html>

    This is exactly equivalent to the previous example. Skeptics are welcome to confirm this by checking the result in the project server.

    Finally, notice that in the select command, the tag name 'h1 is written with a quote mark, whereas doc is not. This is an easy place to get tripped up, but the rule is simple: you don’t use a quote mark when you’re referring to the name of an existing function or variable (like select or doc). But you do need a quote mark when you’re using the text as a literal value.

    Racket (and hence Pollen) makes a distinction between Symbols (e.g. 'h1) and Strings (e.g. "h1"). Without getting into the weeds, just note for now that the tag of an X-expression is always a symbol, not a string. But if you write ◊(select "h1" doc), the command will still work, because Pollen will treat it as ◊(select 'h1 doc), consistent with a general policy of not being persnickety about input types when the intention is clear.

    6.4.4 Linking to an external CSS file

    If you’re a super web hotshot, you probably don’t put your CSS selectors in the <head> tag. Instead, you link to an external CSS file. So it will not surprise you that in Pollen, you can do this by adding the usual <link> tag to your HTML template, in this case a file called styles.css:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊select['h1 doc], by MB</title></head>
    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />
    <body>◊->html[doc]</body>
    </html>

    Fans of hand-coded CSS, I trust you to take it from here: make your styles.css file, and enjoy the results.

    But folks who paid attention during the First tutorial might be wondering “Can we link to a dynamically generated styles.css.pp file?”

    Yes, of course. Here’s the rule of thumb: when you’re making links between files — whether CSS, or HTML, or anything else — Pollen doesn’t care whether the file is static or dynamic. You just refer to it by its ultimate name, in this case styles.css. If a static styles.css file exists, Pollen will use that. If it doesn’t, Pollen will look for a source file it can use to make styles.css, and generate it on the spot. (You can also start with a static file, and change it to be dynamic later, and Pollen will do the right thing.)

    So to use a dynamic CSS file, we don’t need to make any changes to template.html. We just need to add styles.css.pp to the project directory:

    "styles.css.pp"
    #lang pollen
     
    (define h1-color "blue")
    (define strong-color "green")
     
    body {padding: 3em; font-size: 20px;}
    h1 {background: ◊|h1-color|; color: white;}
    strong {color: ◊|strong-color|;}

    Now, when you refresh article.html in the project server, Pollen will generate the styles.css file it needs, and you’ll see the new colors in the page. As before, if you update styles.css.pp, Pollen will notice and regenerate the CSS file when you refresh the page.

    Can you add multiple dynamic style sheets? Yes.
    Can you mix dynamic and static style sheets? Yes.
    Can you add a dynamic JavaScript file? Yes. -
    You’re getting the general idea, right? So let’s move on.

    6.5 Intermission

    If you only need one page for your article, you can stop here. You now know everything necessary to publish a single-page article using authoring mode. You know how to create the mandatory ingredients — a source file and a template — and you also know how to link to an optional CSS file, which can be dynamically generated.

    If you want to create a multi-page article, however, you need to get through one more big idea. This might be a good time to take a break.

    6.6 Pagetrees

    A pagetree is a hierarchical list of Pollen pages. When you have multiple pages in your project, the pagetree establishes relationships among those pages. At its most basic, a pagetree establishes a linear sequence for the pages. But pagetrees can also establish hierarchical relationships — for instance, a book-length project can be organized into chapters, the chapters into sections, and so on. The pagetree doesn’t impose any semantics on the organization of your project. It’s just a tree, and it’s up to you how many layers to establish, what those layers mean, and so on.

    Pagemap might’ve been an equally good name, and perhaps more consistent with similar concepts in other web-publishing systems. But I avoided it out of deference to Racket’s map function, which means something completely different.

    6.6.1 Pagetree navigation

    Pagetrees are used in various ways throughout Pollen. But the most obvious use for a pagetree is to add navigational links to your pages. Obviously, in a multi-page article, readers need a way of getting from one page to the next. In this part of the tutorial, we’ll expand our sample article from one page to three, and see how to create “previous page” and “next page” links in our template that are dynamically generated relative to the current page.

    6.6.2 Using the automatic pagetree

    You’ve actually already been exposed to pagetrees (though I didn’t tell you about it at the time). Recall that the dashboard of the project server is located at http://localhost:8080/index.ptree. The list of files you see in the dashboard is a pagetree that Pollen creates by reading the files in the current directory and arranging them in alphabetical order.

    If the multiple pages in your project are already ordered by filename, then you can rely on this automatic pagetree.

    From earlier in the tutorial, you have a Markdown source file called article.html.pmd that looks like this:

    "article.html.pmd"

    #lang pollen

     

    Deep Thought

    ============

     

    I am **so** happy to be writing this.

    Let’s supplement this source file by creating two others for the project:

    "barticle.html.pmd"

    #lang pollen

     

    Barticle Title

    ==============

     

    The wonderful second part of the article.

    "carticle.html.pmd"

    #lang pollen

     

    Carticle Title

    ==============

     

    The terrific third part.

    As before, you can fill these source files with any sample Markdown content you like. Moreover, you don’t have to use the filenames barticle.html.pmd and carticle.html.pmd — the point is that the intended sequence needs to match the alphabetic sorting of the filenames.

    We’ll reuse the template.html and styles.css files from earlier in the tutorial. Move or delete the other tutorial files so that your dashboard in the project server shows only these five files:

    • article.html.pmd

    • barticle.html.pmd

    • carticle.html.pmd

    • styles.css (or styles.css.pp)

    • template.html

    If you click on any of the three Markdown sources, you will see it converted into HTML using template.html, with styles appiled from styles.css.

    The automatic pagetree for this project is exactly what you see in the dashboard: a list of the three article files, followed by styles.css and template.html.

    6.6.3 Adding navigation links to the template with here

    Recall from earlier in the tutorial that the content of your source file is made available in the template through the special variable doc. Likewise, the name of the current source file is made available through the special variable here.

    To make any navigation link — up, down, sideways — the general idea is that we use here as input to a pagetree-navigation function, which then looks up the answer in the current pagetree.

    First, let’s just see here on its own. Update your template as follows:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊select['h1 doc], by MB</title></head>

    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />

    <body>◊->html[doc]

    The current page is called ◊|here|.

    </body>

    </html>

    If you refresh article.html, you will now see the line “The current page is called article.html.” Switch to barticle.html, and you’ll see “The current page is called barticle.html.” Makes sense, right?

    Now let’s use pagetree functions to show the names of the previous and next pages. Consistent with the usual policy of obviousness, these functions are called previous and next:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊select['h1 doc], by MB</title></head>

    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />

    <body>◊->html[doc]

    The current page is called ◊|here|.

    The previous is ◊|(previous here)|.

    The next is ◊|(next here)|.

    </body>

    </html>

    Refresh barticle.html. You’ll now see that “The current page is called barticle.html. The previous is article.html. The next is carticle.html.” So far, so good: we’re correctly deriving the previous and next pages from the automatic pagetree.

    All that’s left is to add hyperlinks, which is easy:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊select['h1 doc], by MB</title></head>

    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />

    <body>◊->html[doc]

    The current page is called ◊|here|.

    The previous is <a href="◊|(previous here)|">◊|(previous here)|</a>.

    The next is <a href="◊|(next here)|">◊|(next here)|</a>.

    </body>

    </html>

    Refresh barticle.html, and you’ll see that the names of the previous and next pages are now hyperlinks to those pages. Click through and convince yourself that it works.

    The documentation for pagetree Navigation will tell you about the other functions available for generating navigation links.

    6.6.4 Handling navigation boundaries with conditionals

    If you clicked through to article.html or carticle.html, you might’ve noticed a couple problems. Because article.html is the first page in the automatic pagetree, it doesn’t have any previous page it can link to. And the next-page link for carticle.html is styles.css, which is strictly correct — it is, in fact, the next file in the automatic pagetree — but it’s not part of our article, so we’d rather stop the navigation there.

    One way to fix the problem would be to have three separate template files — the standard one with both previous- and next-page links, one with only a next-page link, and one with only a previous-page link.

    But since we have a whole programming language available in Pollen, that’s a dull-witted way to solve the problem. The better way is to add conditionals to the template to selectively change the navigation. That keeps things simple, because we’ll still have only one template.html to deal with.

    To handle article.html, we want to hide the previous-page navigation link when there’s no previous page. As it turns out, if the previous function can’t find a previous page, it will return false. So we just need to wrap our previous-page navigation in the when/block command like so:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊select['h1 doc], by MB</title></head>

    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />

    <body>◊->html[doc]

    The current page is called ◊|here|.

    ◊when/block[(previous here)]{The previous is <a href="◊|(previous here)|">◊|(previous here)|</a>.}

    The next is <a href="◊|(next here)|">◊|(next here)|</a>.

    </body>

    </html>

    The basic structure of when/block is ◊when/block[condition]{insert-this-text}. Note the square braces around the condition, and the curly braces around the text. Using (previous here) as the condition is shorthand for “when (previous here) does not return false...”

    Programmers in the audience might be getting anxious about the repeated use of (previous here) — you’re welcome to store that value in a variable, and everything will work the same way:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊select['h1 doc], by MB</title></head>

    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />

    <body>◊->html[doc]

    The current page is called ◊|here|.

    ◊(define prev-page (previous here))

    ◊when/block[prev-page]{The previous is <a href="◊|prev-page|">◊|prev-page|</a>.}

    The next is <a href="◊|(next here)|">◊|(next here)|</a>.

    </body>

    </html>

    We need a different technique for handling the end of the next-page navigation, because we’re not reaching the actual end of the pagetree. We’re just reaching the end of the pages we care about navigating through.

    What condition will help us detect this? Here, we can notice that the names of our article pages all contain the string article. While you’d probably want a more robust condition for a real project, in this tutorial, what we’ll do is hide the next-page navigation if the name of the next page doesn’t contain “article”. As we did before, we wrap our navigation line in the when/block function:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊select['h1 doc], by MB</title></head>

    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />

    <body>◊->html[doc]

    The current page is called ◊|here|.

    ◊(define prev-page (previous here))

    ◊when/block[prev-page]{The previous is <a href="◊|prev-page|">◊|prev-page|</a>.}

    ◊when/block[(regexp-match "article" (->string (next here)))]{

    The next is <a href="◊|(next here)|">◊|(next here)|</a>.}

    </body>

    </html>

    This time, the condition is (regexp-match "article" (->string (next here))). How were you supposed to know this? You weren’t. That’s why this is a tutorial. Without going on a lengthy detour, the regexp-match function returns true if the first string (in this case, "article") is found inside the second string (in this case, we convert (next here) to a string by wrapping it in ->string).

    In any case, even if some of the programmy bits went over your head just now, relax and paste the code into your template. What you’ll see when you refresh carticle.html is that the next-page link is gone. So now our template lets us navigate among the pages of our article, and the conditionals handle the end pages correctly.

    6.6.5 Making a pagetree file

    I didn’t want to dwell on programming complications in the last conditional. Why? The extra programming was necessary only because we made life somewhat difficult for ourselves by relying on the automatic pagetree. A better way to solve the problem is to avoid it altogether by making a pagetree file.

    Pagetree source files have a different syntax and status than other Pollen source files, so they are parsed using their own Pollen dialect. To invoke this dialect, you just start the file with #lang pollen and name the file with the ptree extension, for instance my-project.ptree. While you can have as many pagetrees in your project as you want, Pollen will accord primary status to the one named index.ptree.

    So let’s make an index.ptree file. At its simplest, a pagetree file can just be a list of files in the intended order. In DrRacket, create a new file in your project directory as follows:

    "index.ptree"

    #lang pollen

     

    carticle.html

    article.html

    barticle.html

    Now run the file. The result will be:

    ’(pagetree-root carticle.html article.html barticle.html)

    Pretty boring, I know. But behind the scenes, Pollen’s pagetree parser is making sure your tree is valid (e.g., no duplicate or malformed names). Today it’s boring, but on the day you have a long and complicated pagetree, you will be grateful.

    Notice that the names in this pagetree are the names of output files, not source files. This is deliberate, so that neither you nor Pollen has to care which files are static vs. dynamic. This next pagetree wouldn’t be wrong in the sense of bad syntax — the pagetree parser won’t complain — but it would be wrong in the sense of not-what-you-want, because it refers to source names rather than output names:

    "bad-index.ptree"

    #lang pollen

     

    carticle.html.pmd

    article.html.pmd

    barticle.html.pmd

    You also probably noticed that the files are in a different order than they were in the automatic pagetree: carticle.html is first, followed by article.html and then barticle.html. This too is deliberate, so we can see what happens with a differently ordered pagetree.

    Pagetrees don’t change nearly as often as other source files, so as a performance optimization, the project server does not dynamically reflect changes to pagetrees. To see the effect of this new pagetree on our project, you’ll need to go to your terminal window and stop the project server with ctrl+C, and then restart it. Which will take all of three seconds.

    Now refresh carticle.html. You’ll notice that the navigation links are different. You won’t see a previous-page link — because carticle.html is now the first page in the pagetree — and the next page will show up as article.html. Click through to article.html, and you’ll see the navigation likewise updated. Click through to barticle.html, and you’ll see ...

    BAM! An error page with a yellow box that says Can’t convert #f to string. What happened? We switched to using our own pagetree file but we didn’t update our template conditionals. Once you reach barticle.html, the value of (next here) is false, which means the (->string (next here)) command in the template conditional is trying to convert false into a string. Hence the error.

    So let’s go back and fix that. Because we don’t have extraneous files in our pagetree anymore, we can change the second conditional in the template to work the same way as the first:

    "template.html"

    <html>

    <head><meta charset="UTF-8">

    <title>◊select['h1 doc], by MB</title></head>

    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />

    <body>◊->html[doc]

    The current page is called ◊|here|.

    ◊(define prev-page (previous here))

    ◊when/block[prev-page]{The previous is <a href="◊|prev-page|">◊|prev-page|</a>.}

    ◊(define next-page (next here))

    ◊when/block[next-page]{

    The next is <a href="◊|next-page|">◊|next-page|</a>.}

    </body>

    </html>

    Refresh barticle.html — because you’re updating the template, you don’t need to restart the project server — and you’ll see the right result. The previous-page link goes to article.html, and the next-page link is hidden.

    6.6.6 index.ptree & the project server

    One more thing to show you before we wrap up this tutorial. Remember that the dashboard of the project server is at http://localhost:8080/index.ptree? By default, the project server will synthesize a pagetree from an alphbetical directory listing.

    But if you put your own index.ptree file in the directory, the project server will use that for the dashboard instead. In fact, visit http://localhost:8080/index.ptree now and you’ll see what I mean. Consistent with the index.ptree you made, you’ll now see carticle.html, article.html, and barticle.html, but not template.html nor styles.css (even though they’re still in the project directory).

    6.7 Second tutorial complete

    That was a big tutorial. I commend you for your tenacity and patience. But in this tutorial, you made a giant leap forward. Despite the silly examples, you now know everything you need to make multi-page articles — books, even — using Markdown authoring mode in Pollen. If this is all you ever use Pollen for, it’ll be a big improvement over ordinary Markdown.

    But there’s more. We haven’t even gotten into the more elaborate automation that’s possible with Pollen, nor Pollen’s own markup language. We’ll cover that in the third tutorial.

     
    \ No newline at end of file +
    You’re getting the general idea, right? So let’s move on.

    6.5 Intermission

    If you only need one page for your article, you can stop here. You now know everything necessary to publish a single-page article using authoring mode. You know how to create the mandatory ingredients — a source file and a template — and you also know how to link to an optional CSS file, which can be dynamically generated.

    If you want to create a multi-page article, however, you need to get through one more big idea. This might be a good time to take a break.

    6.6 Pagetrees

    A pagetree is a hierarchical list of Pollen pages. When you have multiple pages in your project, the pagetree establishes relationships among those pages. At its most basic, a pagetree establishes a linear sequence for the pages. But pagetrees can also establish hierarchical relationships — for instance, a book-length project can be organized into chapters, the chapters into sections, and so on. The pagetree doesn’t impose any semantics on the organization of your project. It’s just a tree, and it’s up to you how many layers to establish, what those layers mean, and so on.

    Pagemap might’ve been an equally good name, and perhaps more consistent with similar concepts in other web-publishing systems. But I avoided it out of deference to Racket’s map function, which means something completely different.

    6.6.1 Pagetree navigation

    Pagetrees are used in various ways throughout Pollen. But the most obvious use for a pagetree is to add navigational links to your pages. Obviously, in a multi-page article, readers need a way of getting from one page to the next. In this part of the tutorial, we’ll expand our sample article from one page to three, and see how to create “previous page” and “next page” links in our template that are dynamically generated relative to the current page.

    6.6.2 Using the automatic pagetree

    You’ve actually already been exposed to pagetrees (though I didn’t tell you about it at the time). Recall that the dashboard of the project server is located at http://localhost:8080/index.ptree. The list of files you see in the dashboard is a pagetree that Pollen creates by reading the files in the current directory and arranging them in alphabetical order.

    If the multiple pages in your project are already ordered by filename, then you can rely on this automatic pagetree.

    From earlier in the tutorial, you have a Markdown source file called article.html.pmd that looks like this:

    "article.html.pmd"
    #lang pollen
     
    Deep Thought
    ============
     
    I am **so** happy to be writing this.

    Let’s supplement this source file by creating two others for the project:

    "barticle.html.pmd"
    #lang pollen
     
    Barticle Title
    ==============
     
    The wonderful second part of the article.
    "carticle.html.pmd"
    #lang pollen
     
    Carticle Title
    ==============
     
    The terrific third part.

    As before, you can fill these source files with any sample Markdown content you like. Moreover, you don’t have to use the filenames barticle.html.pmd and carticle.html.pmd — the point is that the intended sequence needs to match the alphabetic sorting of the filenames.

    We’ll reuse the template.html and styles.css files from earlier in the tutorial. Move or delete the other tutorial files so that your dashboard in the project server shows only these five files:

    • article.html.pmd

    • barticle.html.pmd

    • carticle.html.pmd

    • styles.css (or styles.css.pp)

    • template.html

    If you click on any of the three Markdown sources, you will see it converted into HTML using template.html, with styles appiled from styles.css.

    The automatic pagetree for this project is exactly what you see in the dashboard: a list of the three article files, followed by styles.css and template.html.

    6.6.3 Adding navigation links to the template with here

    Recall from earlier in the tutorial that the content of your source file is made available in the template through the special variable doc. Likewise, the name of the current source file is made available through the special variable here.

    To make any navigation link — up, down, sideways — the general idea is that we use here as input to a pagetree-navigation function, which then looks up the answer in the current pagetree.

    First, let’s just see here on its own. Update your template as follows:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊select['h1 doc], by MB</title></head>
    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />
    <body>◊->html[doc]
    The current page is called ◊|here|.
    </body>
    </html>

    If you refresh article.html, you will now see the line “The current page is called article.html.” Switch to barticle.html, and you’ll see “The current page is called barticle.html.” Makes sense, right?

    Now let’s use pagetree functions to show the names of the previous and next pages. Consistent with the usual policy of obviousness, these functions are called previous and next:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊select['h1 doc], by MB</title></head>
    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />
    <body>◊->html[doc]
    The current page is called ◊|here|.
    The previous is ◊|(previous here)|.
    The next is ◊|(next here)|.
    </body>
    </html>

    Refresh barticle.html. You’ll now see that “The current page is called barticle.html. The previous is article.html. The next is carticle.html.” So far, so good: we’re correctly deriving the previous and next pages from the automatic pagetree.

    All that’s left is to add hyperlinks, which is easy:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊select['h1 doc], by MB</title></head>
    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />
    <body>◊->html[doc]
    The current page is called ◊|here|.
    The previous is <a href="◊|(previous here)|">◊|(previous here)|</a>.
    The next is <a href="◊|(next here)|">◊|(next here)|</a>.
    </body>
    </html>

    Refresh barticle.html, and you’ll see that the names of the previous and next pages are now hyperlinks to those pages. Click through and convince yourself that it works.

    The documentation for pagetree Navigation will tell you about the other functions available for generating navigation links.

    6.6.4 Handling navigation boundaries with conditionals

    If you clicked through to article.html or carticle.html, you might’ve noticed a couple problems. Because article.html is the first page in the automatic pagetree, it doesn’t have any previous page it can link to. And the next-page link for carticle.html is styles.css, which is strictly correct — it is, in fact, the next file in the automatic pagetree — but it’s not part of our article, so we’d rather stop the navigation there.

    One way to fix the problem would be to have three separate template files — the standard one with both previous- and next-page links, one with only a next-page link, and one with only a previous-page link.

    But since we have a whole programming language available in Pollen, that’s a dull-witted way to solve the problem. The better way is to add conditionals to the template to selectively change the navigation. That keeps things simple, because we’ll still have only one template.html to deal with.

    To handle article.html, we want to hide the previous-page navigation link when there’s no previous page. As it turns out, if the previous function can’t find a previous page, it will return false. So we just need to wrap our previous-page navigation in the when/block command like so:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊select['h1 doc], by MB</title></head>
    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />
    <body>◊->html[doc]
    The current page is called ◊|here|.
    when/block[(previous here)]{The previous is
    <a href="◊|(previous here)|">◊|(previous here)|</a>.}
    The next is <a href="◊|(next here)|">◊|(next here)|</a>.
    </body>
    </html>

    The basic structure of when/block is ◊when/block[condition]{insert-this-text}. Note the square braces around the condition, and the curly braces around the text. Using (previous here) as the condition is shorthand for “when "(previous here)" does not return false...”

    Programmers in the audience might be getting anxious about the repeated use of (previous here) — you’re welcome to store that value in a variable, and everything will work the same way:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊select['h1 doc], by MB</title></head>
    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />
    <body>◊->html[doc]
    The current page is called ◊|here|.
    (define prev-page (previous here))
    when/block[prev-page]{The previous is
    <a href="◊|prev-page|">◊|prev-page|</a>.}
    The next is <a href="◊|(next here)|">◊|(next here)|</a>.
    </body>
    </html>

    We need a different technique for handling the end of the next-page navigation, because we’re not reaching the actual end of the pagetree. We’re just reaching the end of the pages we care about navigating through.

    What condition will help us detect this? Here, we can notice that the names of our article pages all contain the string article. While you’d probably want a more robust condition for a real project, in this tutorial, what we’ll do is hide the next-page navigation if the name of the next page doesn’t contain “article”. As we did before, we wrap our navigation line in the when/block function:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊select['h1 doc], by MB</title></head>
    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />
    <body>◊->html[doc]
    The current page is called ◊|here|.
    (define prev-page (previous here))
    when/block[prev-page]{The previous is
    <a href="◊|prev-page|">◊|prev-page|</a>.}
    when/block[(regexp-match "article" (->string (next here)))]{
    The next is <a href="◊|(next here)|">◊|(next here)|</a>.}
    </body>
    </html>

    This time, the condition is (regexp-match "article" (->string (next here))). How were you supposed to know this? You weren’t. That’s why this is a tutorial. Without going on a lengthy detour, the regexp-match function returns true if the first string (in this case, "article") is found inside the second string (in this case, we convert (next here) to a string by wrapping it in ->string).

    In any case, even if some of the programmy bits went over your head just now, relax and paste the code into your template. What you’ll see when you refresh carticle.html is that the next-page link is gone. So now our template lets us navigate among the pages of our article, and the conditionals handle the end pages correctly.

    6.6.5 Making a pagetree file

    I didn’t want to dwell on programming complications in the last conditional. Why? The extra programming was necessary only because we made life somewhat difficult for ourselves by relying on the automatic pagetree. A better way to solve the problem is to avoid it altogether by making a pagetree file.

    Pagetree source files have a different syntax and status than other Pollen source files, so they are parsed using their own Pollen dialect. To invoke this dialect, you just start the file with #lang pollen and name the file with the ptree extension, for instance my-project.ptree. While you can have as many pagetrees in your project as you want, Pollen will accord primary status to the one named index.ptree.

    So let’s make an index.ptree file. At its simplest, a pagetree file can just be a list of files in the intended order. In DrRacket, create a new file in your project directory as follows:

    "index.ptree"
    #lang pollen
     
    carticle.html
    article.html
    barticle.html

    Now run the file. The result will be:

    '(pagetree-root carticle.html article.html barticle.html)

    Pretty boring, I know. But behind the scenes, Pollen’s pagetree parser is making sure your tree is valid (e.g., no duplicate or malformed names). Today it’s boring, but on the day you have a long and complicated pagetree, you will be grateful.

    Notice that the names in this pagetree are the names of output files, not source files. This is deliberate, so that neither you nor Pollen has to care which files are static vs. dynamic. This next pagetree wouldn’t be wrong in the sense of bad syntax — the pagetree parser won’t complain — but it would be wrong in the sense of not-what-you-want, because it refers to source names rather than output names:

    "bad-index.ptree"
    #lang pollen
     
    carticle.html.pmd
    article.html.pmd
    barticle.html.pmd

    You also probably noticed that the files are in a different order than they were in the automatic pagetree: carticle.html is first, followed by article.html and then barticle.html. This too is deliberate, so we can see what happens with a differently ordered pagetree.

    Pagetrees don’t change nearly as often as other source files, so as a performance optimization, the project server does not dynamically reflect changes to pagetrees. To see the effect of this new pagetree on our project, you’ll need to go to your terminal window and stop the project server with ctrl+C, and then restart it. Which will take all of three seconds.

    Now refresh carticle.html. You’ll notice that the navigation links are different. You won’t see a previous-page link — because carticle.html is now the first page in the pagetree — and the next page will show up as article.html. Click through to article.html, and you’ll see the navigation likewise updated. Click through to barticle.html, and you’ll see ...

    BAM! An error page with a yellow box that says Can’t convert #f to string. What happened? We switched to using our own pagetree file but we didn’t update our template conditionals. Once you reach barticle.html, the value of (next here) is false, which means the (->string (next here)) command in the template conditional is trying to convert false into a string. Hence the error.

    So let’s go back and fix that. Because we don’t have extraneous files in our pagetree anymore, we can change the second conditional in the template to work the same way as the first:

    "template.html"
    <html>
    <head><meta charset="UTF-8">
    <title>◊select['h1 doc], by MB</title></head>
    <link rel="stylesheet" type="text/css" media="all" href="styles.css" />
    <body>◊->html[doc]
    The current page is called ◊|here|.
    (define prev-page (previous here))
    when/block[prev-page]{The previous is
    <a href="◊|prev-page|">◊|prev-page|</a>.}
    (define next-page (next here))
    when/block[next-page]{
    The next is <a href="◊|next-page|">◊|next-page|</a>.}
    </body>
    </html>

    Refresh barticle.html — because you’re updating the template, you don’t need to restart the project server — and you’ll see the right result. The previous-page link goes to article.html, and the next-page link is hidden.

    6.6.6 index.ptree & the project server

    One more thing to show you before we wrap up this tutorial. Remember that the dashboard of the project server is at http://localhost:8080/index.ptree? By default, the project server will synthesize a pagetree from an alphbetical directory listing.

    But if you put your own index.ptree file in the directory, the project server will use that for the dashboard instead. In fact, visit http://localhost:8080/index.ptree now and you’ll see what I mean. Consistent with the index.ptree you made, you’ll now see carticle.html, article.html, and barticle.html, but not template.html nor styles.css (even though they’re still in the project directory).

    6.7 Second tutorial complete

    That was a big tutorial. I commend you for your tenacity and patience. But in this tutorial, you made a giant leap forward. Despite the silly examples, you now know everything you need to make multi-page articles — books, even — using Markdown authoring mode in Pollen. If this is all you ever use Pollen for, it’ll be a big improvement over ordinary Markdown.

    But there’s more. We haven’t even gotten into the more elaborate automation that’s possible with Pollen, nor Pollen’s own markup language. We’ll cover that in the third tutorial.

     
    \ No newline at end of file diff --git a/doc/third-tutorial.html b/doc/third-tutorial.html new file mode 100644 index 0000000..4425548 --- /dev/null +++ b/doc/third-tutorial.html @@ -0,0 +1,7 @@ + +7 Third tutorial
    6.1.0.5

    7 Third tutorial

    Now you’re getting to the good stuff. In this tutorial, you’ll use Pollen to publish a multi-page article written in Pollen markup. You’ll learn about:

    • Adding tags & attributes with Pollen markup

    • Attaching behavior to tag functions

    • the directory-require.rkt file

    • raco pollen render and raco pollen clone

    If you want the shortest possible introduction to Pollen, try the Quick tour.

    7.1 Prerequisites

    I’ll assume you’ve completed the Second tutorial and that you understand the principles of Pollen authoring mode — creating source files, converting them to X-expressions, and then combining them with templates to make output files.

    Because now it’s time to pick up the pace. You’ve learned how to do some handy things with Pollen. But we haven’t yet exploited the full fusion of writing environment and programming language. I promised you that The book is a program, right? So let’s do some programming.

    7.2 Pollen markup vs. XML

    You can skip this section if XML holds no interest. But Pollen markup evolved out of my attempt to come up with an alternative to XML that would be more usable for writing. So if you’re familiar with XML, the contrast may be helpful.

    7.2.1 The XML problem

    In the Second tutorial, I made the case that Markdown is a limiting format for authors. Why? Markdown is essentially a notation system for HTML tags. As such, it has three problems: it’s not semantic, it only covers a limited subset of HTML tags, and it can’t be extended by an author.

    These problems are partly limitations of HTML itself. And these limitations were meant to be cured by XML — the X stands for extensible. In principle, XML allows you to define whatever tags you like and use them in your document.

    So why hasn’t XML taken over the world? In practice, XML promises more than it delivers. The reasons are apparent to any writer who’s attempted to use XML as an authoring format:

    • Verbose syntax. Unfortunately, XML relies on the same angle-bracket notation as HTML. If you think HTML source is hard to read, XML is even worse. Since much of writing involves reading, this feature is also a major bug.

    • Validation overhead. Integral to XML is the concept of validation, which guarantees that a document meets certain formal criteria, usually asserted in a schema. To get the full value from XML, you generally want to use validation. But doing so imposes a lot more work on you as an author, and removes much of the expressive potential of XML.

    • Masochistic document processing. I’m referring to XSLT, the preferred method of transforming XML documents. I know a little XSLT, so I’ll concede that there’s a method to its madness. But it’s still madness.

    The nicest thing we could say about XML is that its intentions are good. It’s oriented toward the right goals. But its benefits are buried under atrocious ergonomics.

    7.2.2 What Pollen markup does differently

    Pollen markup can be seen as a way of reaping the benefits of XML without incurring the headaches. Like XML, Pollen markup allows you to freely tag your text. But unlike XML:

    • Simple syntax. Pollen markup follows the usual conventions of Pollen commands.

    • No structural validation. You can use any tags you want, in any order, and you needn’t define them ahead of time. Your document will still work.

    • Racket processing. Pollen markup tags can have behavior attached to them using Racket functions, either before you use them, or later.

    7.2.3 “But I really need XML…”

    You can have XML. There’s nothing wrong with using Pollen markup to generate XML files that can then be fed into an existing XML processing pipeline. In other words, using Pollen markup, you can treat XML as an output format rather than an input format.

    In this tutorial, I’ll be rendering Pollen markup with an HTML template. But you could easily use the same workflow with an XML template and thus end up with XML files.

    7.3 Writing with Pollen markup

    Pollen markup is a free-form markup system that lets you add arbitrary tags and attributes to your text. By arbitrary, I mean that they don’t need to match up with an existing schema or specification (e.g., the tags permitted by HTML). They can — but that’s an option, not a requirement.

    I like to think of Pollen markup a way of capturing not just the text, but also my ideas about the text. Some of these are low-level ideas (“this text should be italicized”). Some are high-level ideas (“this text is the topic of the page”). Some are just notes to myself. In short, everything I know about the text becomes part of the text.

    In so doing, Pollen markup becomes the source code of the book. Let’s try it out.

    7.3.1 Creating a Pollen markup file

    We’re going to use Pollen markup to make a file that will ultimately be HTML. So consistent with the authoring-mode workflow we learned in the Second tutorial, we’ll start with our desired output filename, article.html, and then append the Pollen markup suffix, .pm.

    In DrRacket, start a new file called article.html.pm like so (BTW you can use any sample text you like):

    "article.html.pm"
    #lang pollen
     
    I want to attend RacketCon this year.

    Consistent with usual authoring-mode policy, when you run this file, you’ll get an X-expression that starts with root:

    '(root "I want to attend RacketCon this year.")

    Remember, even though the first line of the file is #lang pollen — same as the last tutorial — the new .pm suffix signals that Pollen should interpret the source as Pollen markup. Look what happens if you goof up and put Markdown source in a Pollen markup file, like so:

    #lang pollen
     
    I am **so** excited to attend __RacketCon__ this year.

    The Markdown syntax will be ignored, and pass through to the output:

    '(root "I am **so** excited to attend __RacketCon__ this year.")

    Restore the non-Markdown source, and let’s continue.

    7.3.2 Tags & tag functions

    Pollen markup uses the same Pollen command syntax that we first saw in Adding commands. Previously, we used this command syntax to invoke functions like define and ->html. Pollen markup is used to invoke a special kind of function called a tag function, which is a function that, by default, adds a tag to the text.

    To see how this works, restore your article.html.pm file to its original state:

    #lang pollen
     
    I want to attend RacketCon this year.

    We can add any tag with Pollen markup, but for now, let’s start with an old favorite: em, which is used in HTML to add emphasis to text. We apply a tag by starting with the lozenge character (◊) followed by the tag name em, followed by the text in curly braces, like so:

    "article.html.pm"
    #lang pollen
     
    I want to attend em{RacketCon this year}.

    Run this file in DrRacket and see the X-expression that results:

    '(root "I want to attend " (em "RacketCon this year") ".")

    You won’t be surprised to hear that you can nest tags:

    "article.html.pm"
    #lang pollen
     
    I want to attend em{RacketCon strong{this} year}.

    With the expected results:

    '(root "I want to attend " (em "RacketCon " (strong "this") " year") ".")

    7.3.3 Attributes

    Attributes are like tags for tags. Each attribute is a key–value pair where the key is any name, and the value is a string. Anyone who’s seen HTML is familiar with them:

    <span class="author">Prof. Leonard</span>

    Here, class is an attribute for span that has value "author". And this is what it looks like as an X-expression:

    '(span ((class "author")) "Prof. Leonard")

    You can add any number of attributes to a tag (first as an X-expression, then as HTML):

    '(span ((class "author")(id "primary")(living "true")) "Prof. Leonard")

    <span class="author" id="primary" living="true">Prof. Leonard</span>

    In Pollen markup, attributes have the same logic, but a slightly different syntax. In keeping with the tag notation you just saw, the span tag is added in the usual way:

    "article.html.pm"
    #lang pollen
     
    span{Prof. Leonard}

    Then you have two options for adding attributes. The verbose way corresponds to how the attributes appear in the X-expression:

    "article.html.pm"
    #lang pollen
     
    span['((class "author")(id "primary")(living "true"))]{Prof. Leonard}

    Each key–value pair is in parentheses, and then the list of pairs is within parentheses, with a quote (') at the front that signals that the text should be used literally.

    This involves some superfluous typing, however, so Pollen also supports an abbreviated syntax for attributes:

    "article.html.pm"
    #lang pollen
     
    span['class:"author" 'id:"primary" 'living:"true"]{Prof. Leonard}

    In this form, each attribute key starts with a quote mark ' and ends with a colon :. As before, the attribute value is in quotation marks.

    Both of these forms will produce the same X-expression:

    '(span ((class "author")(id "primary")(living "true")) "Prof. Leonard")

    Now that you know how to make tags and attributes, you might wonder whether Pollen markup can be used as a quick & dirty HTML-notation system. Sure — for a quick & dirty project, why not. Recall that X-expressions are just alternative notation for the standard angle-bracket notation used in HTML. So if you wanted HTML like this:

    <div class="red" style="font-size:150%">Important <em>News</em></div>

    You could write it in Pollen markup like so:

    ◊div['class:"red" style:"font-size:150%"]{Important ◊em{News}}

    And then just convert it (using the ->html function) into the HTML above. Thus, the tags you already know and love (?) can be used in Pollen markup, but with fewer keystrokes and cruft.

    Still, if Pollen markup were just an alternative notation system for HTML tags, it would be pretty boring. As I alluded above, that’s merely a boring way to use it.

    In the XML spirit, Pollen markup lets you use any tags you want. That’s considerably less boring.

    7.3.4 What are custom tags good for?

    XML jocks can skip this section, since you already know. But if you’ve been mired in Markdown or HTML, read on.

    Tags, broadly speaking, are a means of annotating a text with extra information, which I’ll call metadata (using that term in its generic sense, not in any fiddly computery way). Metadata is the key tool that enables an author to write a book with the benefits of semantic markup and format independence.

    7.3.5 Semantic markup

    Semantic markup means adding metadata to text according to the meaning of the text, not merely its intended visual appearance. So rather than tagging RacketCon with an em tag, as we did above to indicate how the word should look, maybe we would tag it with an event tag, to indicate what kind of thing it is.

    Semantic markup lets an author specify distinctions that would be ambiguous in pure visual terms, thereby capturing more meaning and intent. For instance, in books, italic styling is commonly applied to a number of unrelated types of information: emphasized words, movie titles, terms being used for the first time, headings, captions and labels, and so on. Under a non-semantic formatting scheme, perhaps one would tag them all em. But in semantic terms, one would tag them movie-title, first-use, heading, as appropriate.

    This has two major benefits. First, by separating appearance and meaning, an author can manage the content of the book in useful ways. For instance, if every movie title were tagged as movie-title rather than italic, then it would be simple to generate a list of all movies mentioned in the book (for the author’s benefit) or a page index of movie references (for the reader’s benefit). But without that semantic tagging, a movie title couldn’t be distinguished from any other italicized text.

    7.3.6 Format independence

    The second benefit of custom tags is format independence, or the ability to change the rendering of the text to suit a particular device or context.

    When a text is encrusted with format-specific visual tags — for instance, HTML tags — then the document markup is entangled with a single output format. If you only need one output format, fine.

    But increasingly, book authors have been called upon to publish their work in multiple formats: paper and PDF, but also web, e-book, or other natively digital formats, that connect to devices with differing display capabilities.

    Yes, I know that many of these formats are based on variants of HTML. But the HTML you can use in a desktop web browser is quite different from, say, the HTML you can use in a Kindle .mobi file. The .mobi file has other technical requirements too, like an .ncx and .opf file. So despite some genetic kinship, these HTML-ish formats are best understood as separate targets.

    Using a display-driven model to manage this complexity is a terrible idea — as anyone who’s tried it can attest. Converting from one display-based file type to another — for instance, word processor to HTML, or HTML to PDF — is an exercise in frustration and drain-circling expectations.

    This isn’t surprising. For a long time, text processing has been dominated by this display-driven model. Most word processors, like Microsoft Word and Pages, have been built around this model. It worked well enough in the era where most documents were eventually going to be printed on paper (or a paper simulator like PDF). HTML was a technical leap forward, but not a conceptual leap: it mostly represented the display options available in a web browser.

    There’s a couple TeX fans at the back of the room, waving their arms. Yes, TeX got a lot of things right. In practice, however, it never became a core tool for electronic publishing (which, to be fair, didn’t exist when TeX was written). Plenty of ideas in Pollen were lifted from TeX.

    For a document to be format independent, two conditions have to be satisfied.

    First, the document has to be readable by other programs, so they can handle the conversion of format-independent markup into a format-specific rendering (e.g., mapping semantic tags like movie-title onto visual tags like em). Most word-processor formats, like Word’s .docx, are bad for authoring because these formats are opaque and proprietary. We needn’t get into the political objections. As a practical matter, they’re inarguably restrictive — if you can’t get your data out of your file, you’re stuck.

    Second, the document itself has to be represented in a way that’s independent of the particularities of any one format. For instance, HTML is a bad authoring format because it encourages authors to litter their text with HTML-isms like h1 and span. These have no meaning outside of HTML, and thus will always cause conversion problems. The same goes for Markdown, which is simply HTML in disguise.

    The solution to the first condition is to use text-based markup rather than proprietary file types. The solution to the second condition is to let authors define custom tags for the document, rather than the other way around. Pollen markup incorporates both of these ideas.

    7.3.7 Using custom tags

    You can insert a custom tag using the same syntax as any other tag. Suppose you want to use an event tag to mark events. You would insert it like so:

    "article.html.pm"
    #lang pollen
     
    I want to attend event{RacketCon} this year.

    This markup will turn into this X-expression:

    '(root "I want to attend " (event "RacketCon") " this year.")

    Which is equivalent to this XML:

    <root>I want to attend <event>RacketCon</event> this year.</root>

    In truth, Pollen doesn’t notice any difference between a custom tag vs. a standard HTML tag vs. any other kind of tag. They’re all just markup tags. If you want to restrict yourself to a certain vocabulary of tags, you can. If you want to set up Pollen to enforce those restrictions, you can do that too. But by default, Pollen doesn’t impose restrictions like this. In general, you can pick any tag name you want, and it will work.

    Don’t take my word for it. See what happens if you write this:

    "article.html.pm"
    #lang pollen
     
    I want to attend verylongandimpracticaltagname{RacketCon} this year.

    One small but important exception to this rule. If you were wondering why I sometimes call them tag functions instead of just tags, it’s because under the hood, every tag is implemented as a function. The default behavior of this function is just to wrap the text in a tag with the given name.

    The benefit of treating tags as functions will become evident later in this tutorial. But the cost of this approach is that tags occupy the same namespace as the other functions available in Pollen (and by extension, Racket). So if you try to use a tag name that’s already the name of an existing function, an error will occur.

    For instance, let’s suppose you try to use a custom tag called length:

    "article.html.pm"
    #lang pollen
     
    The Panama Canal is length{77km} across.

    When you run this file, you get an error:

    length: contract violation;
    expected: list?
      given: "77km"

    The problem is that Racket already provides a function called length. Consistent with the usual rules of Pollen command notation, your command is interpreted as an attempt to invoke the length function, rather than apply a tag named length.

    In practice, namespace clashes are rare. But if necessary, they’re easy to work around (for the simplest method, see Invoking tag functions).

    7.3.8 Choosing custom tags

    You just saw that using custom tags is easy. Choosing custom tags, on the other hand, is less science than art. As the author, it’s up to you. Some guidelines:

    • You’re never doing it wrong. I wanted to make sure you knew the case for semantic markup. But if your life would be easier just using HTML tags directly, go ahead.

    • Tag iteratively. Don’t worry about getting all your tags right the first time through. Just as you write and then rewrite, add the tags that seem right now, and change or augment them later, because …

    • Tags emerge from writing. It’s hopeless to try to specify all your tags in advance. As you write, you’ll learn things about the text, which will suggest new tags.

    • The best tag system is the one you’ll stick with. Tags aren’t free. It takes effort to insert them consistently. Don’t bother with an overambitious tag scheme that bores you more than it helps.

    • For boilerplate, tags are faster than text. If you find yourself repeatedly formatting certain text in a certain way — for instance, lists and tables — extract the content and wrap it in a tag that encapsulates the boilerplate.

    And most important:

    • Tags are functions. As I mentioned above, every tag has a function behind it that uses the content of the tag as input. The default tag function just outputs the tag and its content. But you can replace this with any kind of function. So in practice, you can offload a lot of labor to tags.

    As we’ll see in the next section, this is where your book truly becomes programmable.

    7.4 Tags are functions

    Don’t skip this section! It explains a concept that’s essential to understanding how Pollen works.

    If you’ve used HTML or XML, tags are just tags: things you type into the document that look the same going out as they did going in. Tags can be used to select document elements or assign styling (via CSS). But they don’t have any deeper effect on the document content.

    That’s not so in Pollen. Under the hood, Pollen is just an alternate way of writing code in the Racket programming language. And tags, instead of being inert markers, are actually functions.

    I think most of you know what a function is, but just to be safe — in programming, a function is a chunk of code that accepts some input, processes it, and then returns a value. Asking a function to process some data is known as calling the function.

    Leading us to the Three Golden Rules of Pollen Tags:

    1. Every Pollen tag calls a function with the same name.

    2. The input values for that function are the attributes and content of the tag.

    3. The whole tag — tag name, attributes, and content — is replaced with the return value of the called function.

    You’ve already seen the simplest kind of function in a Pollen document: the default tag function, which emulates the behavior of standard markup tags.

    Let’s revisit an earlier example, now with the help of the Golden Rules:

    "article.html.pm"
    #lang pollen
     
    I want to attend em{RacketCon strong{this} year}.

    What happens when you run this source? Working from the inside out, Pollen calls the function strong with the input "this". The result is (strong "this"). Then Pollen calls the function em with the three input values "RacketCon " (strong "this") " year", which yields (em "RacketCon " (strong "this") " year"). Finally, Pollen calls the root function with everything in the document, resulting in:

    '(root "I want to attend " (em "RacketCon " (strong "this") " year") ".")

    7.4.1 Attaching behavior to tags

    Sometimes this default behavior will suffice. But other times, you’ll want to change the behavior of a tag. Why? Here are some useful examples of what you, as an author, can do with custom tag functions:

    • Automatically detect cross-references and add hyperlinks.

    • Pull in data from an external source.

    • Generate tables, figures, and other fiddly layout objects.

    • Change content based on given conditions.

    • Automatically detect line breaks, paragraphs, and lists.

    • Insert boilerplate text.

    • Anything annoying or repetitive.

    • Mathematical computations.

    • … and anything else you like to do with a programming language.

    Having invited you to gaze across these vistas, I apologize that my example here in this tutorial is necessarily tip-of-the-iceberg. I’ll be adding a more detailed guide to writing Pollen functions, both simple and crafty.

    How do you change the behavior of a tag? By 1) writing a new function and 2) giving it the name of the tag. Once you do this, this new behavior will automatically be invoked when you use the tag.

    For example, let’s redefine the strong tag in our example above to simply print BOOM:

    "article.html.pm"
    #lang pollen
     
    define[(strong . lines)]{BOOM}
     
    I want to attend em{RacketCon strong{this} year}

    When you run this file, you indeed get:

    '(root "I want to attend " (em "RacketCon " "BOOM" " year"))

    How does this work? First, although you can define a function in Pollen command syntax using either of The two command modes: text mode & Racket mode, it tends to be easier to use Racket mode. I wrote the first one in text mode. But for clarity, I’m going to switch to Racket mode (run this file and convince yourself it comes out the same):

    "article.html.pm"
    #lang pollen
     
    (define (strong word) "BOOM")
     
    I want to attend em{RacketCon strong{this} year}.

    Let’s look at our new function definition. As usual, we start with the lozenge character () to denote a Pollen command. Then we use define to introduce a function definition. The name of the function comes next, which needs to match our tag name, strong. The expression (strong word) means “the name of this function is strong, and it takes a single word as input, which we’ll refer to as word.” Finally we have the return value, which is "BOOM".

    Let’s run this file again, but go back to the Golden Rules to understand what happens. Working from the inside out, Pollen calls the function strong with the input "this" — same as before. But this time, the result of the strong function is not (strong "this"), but simply BOOM. Then Pollen calls the function em with the three input values "RacketCon " "BOOM" " year", which yields (em "RacketCon " "BOOM" " year"). Finally, Pollen calls the root function with everything in the document, resulting in:

    '(root "I want to attend " (em "RacketCon " "BOOM" " year"))

    This example is contrived, of course. But the basic idea — defining a function with the name of a tag — is the foundation of programmability in Pollen. If you get this, and the Golden Rules, you get everything.

    7.4.2 Notes for experienced programmers

    Having said that, some of you are probably eager to hack around a bit. Let me chip off a few more cubes from the iceberg to help you on your way. (Everyone else, take five.)

    7.4.2.1 Point of no return

    If you’ve written functions in other programming languages, you might be accustomed to using a return statement to send a value back from the function. This doesn’t exist in Pollen or Racket — the return value of any function is just the last expression evaluated. In the example below, "BAP" becomes the return value because it’s in the last position, and "BOOM" is ignored:

    "article.html.pm"
    #lang pollen
     
    (define (strong word) "BOOM" "BAP")
     
    I want to attend em{RacketCon strong{this} year}.
    7.4.2.2 Multiple input values & rest arguments

    Sometimes a tag will have only one word or string that becomes its input. More likely, however, it will have multiple values (this is inevitable with nested tags, because the results aren’t concatenated). For instance, if we attach our function to em rather than strong:

    "article.html.pm"
    #lang pollen
     
    (define (em word) "BOOM")
     
    I want to attend em{RacketCon strong{this} year}.

    Look what happens:

    em: arity mismatch;
    the expected number of arguments does not match the given number
    expected: 1
      given: 3

    The error arises because the em function is getting three arguments — "RacketCon " "BOOM" " year" — but has been defined to only accept one argument, word. This is the “arity mismatch.”

    To fix this, it’s better to get in the habit of writing tag functions that accept an indefinite number of input values. You do this by defining your function with a rest argument (as in, “give me the rest of the input values.”) To use a rest argument, put it last in your list of input arguments, and add a period . before:

    "article.html.pm"
    #lang pollen
     
    (define (em . parts) "BOOM")
     
    I want to attend em{RacketCon strong{this} year}.

    This time, the source file will run without an error, producing this:

    '(root "I want to attend " "BOOM" ".")

    A rest argument like parts is a list of individual arguments. So if you want to unpack & process these arguments separately, you can use Racket’s extensive list-processing functions (see Pairs and Lists). Also see quasiquote below.

    7.4.2.3 Returning an X-expression

    Often, you won’t use a tag function to replace a whole tag with a string — you’ll replace it with a different tag, described by an X-expression, like so:

    "article.html.pm"
    #lang pollen
     
    (define (em . parts) '(big "BOOM"))
     
    I want to attend em{RacketCon strong{this} year}.

    Which produces:

    '(root "I want to attend " (big "BOOM") ".")

    The quote mark ' before the X-expression signals to Racket that you want to use what follows as a literal value.

    To build X-expressions that are more elaborate, you have two options.

    First is quasiquote. Quasiquote works like quote, but starts with a backtick character `. What makes it “quasi” is that you can insert variables using the unquote operator, which is a comma , or merge a list of values with the unquote-splicing operator, which is a comma followed by an @ sign ,@.

    Let’s adapt the example above to use quasiquote. Suppose we want to take the parts we get as input and put them inside a big tag. This is easy to notate with quasiquote and the unquote-splicing operator, because parts is a list:

    "article.html.pm"
    #lang pollen
     
    (define (em . parts) `(big ,@parts))
     
    I want to attend em{RacketCon strong{this} year}.

    Which produces this:

    '(root "I want to attend " (big "RacketCon " (strong "this") " year") ".")

    Of course you can also nest X-expressions in your return value:

    "article.html.pm"
    #lang pollen
     
    (define (em . parts) `(extra (big ,@parts)))
     
    I want to attend em{RacketCon strong{this} year}.

    The second option for building X-expressions is to use the txexpr: Tagged X-expressions library that’s included with Pollen (see those docs for more information).

    7.4.2.4 Interpolating variables into strings

    The usual way is to use the format function:

    (format "String with variable: ~a" variable-name)

    See the docs for format and fprintf for your options.

    Be careful if you’re working with integers and X-expressions — a raw integer is treated as a character code, not an integer string. Using format is essential:

    Examples:

    > (->html '(div "A raw integer indicates a character code: " 42))

    "<div>A raw integer indicates a character code: &#42;</div>"

    > (->html `(div "Use format to make it a string: " ,(format "~a" 42)))

    "<div>Use format to make it a string: 42</div>"

    7.4.2.5 Parsing attributes

    Detecting attributes in an argument list can be tricky because a) the tag may or may not have attributes, b) those attributes may be in standard or abbreviated syntax. For this reason, Pollen provides a split-attributes function (in the pollen/tag librar) that you can use in custom tag functions to separate the attributes and elements:

    "article.html.pm"
    #lang pollen
     
    (require pollen/tag)
     
    (define (em . parts)
      (define-values (attributes elements) (split-attributes parts))
      `(extra ,attributes (big ,@elements)))
     
    I want to attend em['key: "value"]{RacketCon}.

    This will move the elements inside the big tag, and attach the attributes to the extra tag:

    '(root "I want to attend " (extra ((key "value")) (big "RacketCon")) ".")

    7.5 Intermission

    That was a lot of heavy material. But it also covered the most essential idea in Pollen: that every tag is a function. Congratulations on making it this far.

    The good news is that the rest of this tutorial will feel more relaxed, as we put these new principles to work.

    Sorry that this tutorial is longer than the others, but truly — this is the stuff that makes Pollen different. If you’re not feeling enthusiastic by now, you should bail out.

    Otherwise, get ready to rock.

    7.6 Organizing functions

    In the tag-function examples so far, we’ve defined each function within the source file where we used it. This is fine for quick little functions.

    But more often, you’re going to want to use functions defined elsewhere, and store your own functions available so they’re available to your source files.

    For now, we’re just invoking functions within a Pollen markup file. But as you’ll see in the fourth tutorial, any function can be called from any kind of Pollen source file.

    7.6.1 Using Racket’s function libraries

    Any function in Racket’s extensive libraries can be called by loading the library with the require command, which will make all its functions and constants available with the usual Pollen command syntax:

    "article.html.pm"
    #lang pollen
     
    (require racket/math)
     
    Pi is close to ◊|pi|.
    The hyperbolic sine of pi is close to (sinh pi).

    The result:

    '(root "Pi is close to " 3.141592653589793 "." "\n" "The hyperbolic sine of pi is close to " 11.548739357257748 ".")

    One caveat — you’re still in a Pollen markup file, so the return value of whatever function you call has to produce a string or an X-expression, so it can be merged into the document. This is similar to the restriction introduced in the first tutorial where functions used in preprocessor files had to produce text. +Pollen won’t stop you from calling a function that returns an incompatible value, like plot, which returns a bitmap image:

    "article.html.pm"
    #lang pollen
     
    (require math plot)
     
    Here's a sine wave:
    (plot (function sin (- pi) pi #:label "y = sin(x)"))

    But it won’t work when you try to run it in DrRacket or load it in the project server.

    It would be fine, however, to call a different kind of plot function that returned an SVG result, because any XML-ish data structure can be converted to an X-expression.

    Super web nerds also know that binary data can be converted into XML-ish form by encoding the file as a base-64 data URL — but if you know what I’m talking about, then you don’t need my help to try it.

    For functions that don’t return a string or an X-expression, you can always make a conversion by hand. For instance, consider range, a Racket function that returns a list of integers:

    "article.html.pm"
    #lang pollen
     
    (require racket/list)
    A list of integers: (range 5)

    This will produce an error in DrRacket:

    pollen markup error: in '(root "A list of integers: " (0 1 2 3 4)), '(0 1 2 3 4) is not a valid element (must be txexpr, string, symbol, XML char, or cdata)

    In a case like this, you can explicitly convert the return value to a string (in whatever way makes sense):

    "article.html.pm"
    #lang pollen
     
    (require racket/list racket/string)
    A list of integers: (string-join (map number->string (range 5)))

    And get this output:

    '(root "A list of integers: " "0 1 2 3 4")

    7.6.2 The directory-require.rkt file

    Don’t skip this section! It explains a concept that’s essential to understanding how Pollen works.

    As you get more comfortable attaching behavior to tags using tag functions, you’ll likely want to create some functions that can be shared between multiple source files. The directory-require.rkt file is a special file that is automatically imported by Pollen source files in the same directory. So every function and value provided by directory-require.rkt can be used in these Pollen files.

    First, using this file is not mandatory. You can always import functions and values from another file using require (as seen in the previous section). The directory-require.rkt is just meant to cure the tedium of importing the same file into every Pollen source file in your project. In a small project, not much tedium; in a large project, more.

    Second, notice from the .rkt suffix that directory-require.rkt is a source file containing Racket code, not Pollen code. This is the default because while Pollen is better for text-driven source files, Racket is better for code-driven source files. Still, the choice is yours: the name of this file can be changed by resetting the world:directory-require value.

    Third, notice from the directory- prefix that directory-require.rkt is only used by Pollen source files in the same directory. So if your project has source files nested inside a subdirectory, you’ll need to explicitly create another directory-require.rkt there and share the functions & values as needed.

    “Why not make this file visible throughout a project, rather than just a directory?” Good idea, but I couldn’t figure out how to do it without creating finicky new dependencies. If you have a better idea, I’m open to it.

    Let’s see how this works in practice. In the same directory as article.html.pm, create a new directory-require.rkt file as follows:

    "directory-require.rkt"
    #lang racket
    (define author "Trevor Goodchild")
    (provide author)

    Here we use the define function (which we’ve seen before) to set author equal to "Trevor Goodchild". Note the final step: consistent with standard Racket rules, we have to explicitly provide the new value so that other files can see it (unlike Python, things you define in Racket are private by default, not public).

    Then update good old article.html.pm:

    "article.html.pm"
    #lang pollen
     
    The author is ◊|author|.

    Run this in DrRacket and you’ll get:

    '(root "The author is " "Trevor Goodchild" ".")

    Now, in the same dirctory, create a second Pollen source file:

    "barticle.html.pm"
    #lang pollen
     
    The author is really ◊|author|?

    Run this, and you’ll get:

    '(root "The author is really " "Trevor Goodchild" "?")

    That’s all there is to it. Everything provided by directory-require.rkt is automatically available within each Pollen source file.

    You can include functions, including tag functions, the same way. For instance, add a function for em:

    "directory-require.rkt"
    #lang racket
    (define author "Trevor Goodchild")
    (define (em . parts) `(extra (big ,@parts)))
    (provide author em)

    Then use it in a source file:

    "article.html.pm"
    #lang pollen
     
    The em{author} is em{◊|author|}.

    With the expected results:

    '(root "The " (extra (big "author")) " is " (extra (big "Trevor Goodchild")) ".")

    7.7 Putting it all together

    [Coming soon]

    1 directory-require.rkt +2-4 three source files +5 pagetree +6 template +}

     
    \ No newline at end of file diff --git a/scribblings/command.scrbl b/scribblings/command.scrbl index 785da19..5ec8425 100644 --- a/scribblings/command.scrbl +++ b/scribblings/command.scrbl @@ -297,13 +297,13 @@ For instance, suppose we want to use @tt{map} as a tag even though Racket is usi @racketoutput{@literal{'(my-map "How I would love this to be a map.")}} -But @code{my-map} is not the tag we want. We need to define @code{my-map} to be a tag function for @tt{map}. We can do this with the Pollen helper @racket[make-tag-function]. That function lives in @racket[pollen/tag], so we @racket[require] that too: +But @code{my-map} is not the tag we want. We need to define @code{my-map} to be a tag function for @tt{map}. We can do this with the Pollen helper @racket[make-default-tag-function]. That function lives in @racket[pollen/tag], so we @racket[require] that too: @codeblock|{ #lang pollen ◊(require pollen/tag) -◊(define my-map (make-tag-function 'map)) +◊(define my-map (make-default-tag-function 'map)) ◊my-map{How I would love this to be a map.} }| diff --git a/scribblings/format-test.scrbl b/scribblings/format-test.scrbl new file mode 100644 index 0000000..8d1bb29 --- /dev/null +++ b/scribblings/format-test.scrbl @@ -0,0 +1,47 @@ +#lang scribble/manual + +@(require "mb-tools.rkt") + +@fileblock["hello" @bold{world}] + + +@codeblock|{ +#lang pollen + +◊define[greeting]{Plain @codeblock.}. +}| + + +@filebox["foo.html"]{ +@codeblock|{ +#lang pollen + +◊define[greeting]{@codeblock wrapped in @filebox}. +}|} + + +@racketblock[ +(define greeting + "Plain @racketblock") +] + +@filebox["foo.html"]{ +@racketblock[ +(define greeting + "@racketblock wrapped in @filebox") +]} + +@racketmod[racket/base +(define greeting + "Plain @racketmod") +] + +@racketmod[#:file "foo.html" racket/base +(define greeting + "@racketmod with #:file argument") +] + + +@filebox["foo.html"]{ +Plain #filebox +} \ No newline at end of file diff --git a/scribblings/formats.scrbl b/scribblings/formats.scrbl index 7f672e2..244f559 100644 --- a/scribblings/formats.scrbl +++ b/scribblings/formats.scrbl @@ -53,9 +53,9 @@ Any value or function that is defined within the source file using @racket[defin @bold{How is this different from Racket?} In Racket, you must explicitly @racket[provide] any values you want to export. Unlike Racket, every Pollen source file impliedly uses @racket[(provide (all-defined-out))]. -@subsection{The @code{@(format "~a" world:project-require)} file} +@subsection{The @code{@(format "~a" world:directory-require)} file} -If a file called @code{@(format "~a" world:project-require)} exists in the same directory with a source file, it's automatically imported when the source file is compiled. +If a file called @code{@(format "~a" world:directory-require)} exists in the same directory with a source file, it's automatically imported when the source file is compiled. @bold{How is this different from Racket?} In Racket, you must explicitly import files using @racket[require]. diff --git a/scribblings/mb-tools.rkt b/scribblings/mb-tools.rkt new file mode 100644 index 0000000..53f6fb9 --- /dev/null +++ b/scribblings/mb-tools.rkt @@ -0,0 +1,46 @@ +#lang racket/base +(require scribble/core scribble/base scribble/manual racket/list scribble/private/manual-sprop scribble/decode scribble/html-properties racket/runtime-path racket/string) + +(provide (all-defined-out)) + +(define-runtime-path mb-css "mb.scss") + +(define (racketfont* . args) + (element 'tt args)) + +(define (fileblock filename . inside) + (compound-paragraph + (style "fileblock" (list* (alt-tag "div") 'multicommand + (box-mode "RfileboxBoxT" "RfileboxBoxC" "RfileboxBoxB") + scheme-properties)) + (list + (paragraph (style "fileblock_filetitle" (list* (alt-tag "div") (box-mode* "RfiletitleBox") scheme-properties)) + (list (make-element + (style "fileblock_filename" (list (css-style-addition mb-css))) + (if (string? filename) + (filepath filename) + filename)))) + (compound-paragraph + (style "fileblock_filecontent" (list* (alt-tag "div") (box-mode* "RfilecontentBox") scheme-properties)) + (decode-flow inside))))) + +(define (convert-newlines args) + (map (λ(arg) (if (equal? arg "\n") (linebreak) arg)) args)) + +(define (repl-output . args) + (nested (racketvalfont (racketfont* (convert-newlines args))))) + +(define (errorblock . args) + (nested (racketerror (racketfont* (convert-newlines args))))) + +(define (browser . args) + (compound-paragraph (style "browser" (list (css-style-addition mb-css) (alt-tag "div"))) (list (paragraph (style #f null) (string-append* args))))) + +(define (terminal . args) + (compound-paragraph (style "terminal" (list (css-style-addition mb-css) (alt-tag "div"))) (list (apply verbatim args)))) + + +(define (noskip-note) + (nested #:style (style "noskip" (list (css-style-addition mb-css) (alt-tag "div"))) + (margin-note "Don’t skip this section! It explains a concept that's essential to understanding how Pollen works."))) + \ No newline at end of file diff --git a/scribblings/mb.scss b/scribblings/mb.scss new file mode 100644 index 0000000..ceae050 --- /dev/null +++ b/scribblings/mb.scss @@ -0,0 +1,45 @@ +/* special styles for custom @fileblock function in mb-tools.rkt */ + +.noskip .refcolumn { + background: #998; + color: white; +} + +.noskip .refcontent p { + font-size: 110%; + line-height: 1.4; +} + +.fileblock .SIntrapara { + margin: 0 0 0 0; +} + +.fileblock .SCodeFlow { + padding-top: 0.7em; + margin-top: 0; +} + +.fileblock { + width: 90%; +} + +.fileblock_filetitle{ + background: #eee; + text-align:right; + padding: 0.15em; + border: 1px dotted black; + border-bottom: none; +} + +.terminal .SIntrapara, .browser .SIntrapara { + margin: 0 0 0 0; +} + +.terminal, .browser { + margin-bottom: 1em; + padding: 0.5em; + width: 88%; + background: #fcfcfc; + color: #666; +} + diff --git a/scribblings/pollen.scrbl b/scribblings/pollen.scrbl index 5d95b7b..157a8a6 100644 --- a/scribblings/pollen.scrbl +++ b/scribblings/pollen.scrbl @@ -51,7 +51,7 @@ Or, if you can find a better digital-publishing tool, use that. But I'm never go @include-section["tutorial-second.scrbl"] -@;include-section["tutorial-third.scrbl"] +@include-section["tutorial-third.scrbl"] @include-section["raco.scrbl"] diff --git a/scribblings/quick.scrbl b/scribblings/quick.scrbl index 9627d42..be7f5fd 100644 --- a/scribblings/quick.scrbl +++ b/scribblings/quick.scrbl @@ -1,5 +1,7 @@ #lang scribble/manual +@(require "mb-tools.rkt") + @title[#:tag "quick-tour"]{Quick tour} @(define (link-tt url) (link url (tt url))) @@ -11,7 +13,9 @@ Assuming you've installed Racket & Pollen, launch DrRacket. Open a new document. Change the top line to: -@racketmod[pollen] +@codeblock{ +#lang pollen +} The first line of every Pollen source file will start with @tt{#lang pollen}. @@ -20,12 +24,14 @@ The first line of every Pollen source file will start with @tt{#lang pollen}. Add a second line to your source file so it reads: -@racketmod[pollen -Hello world] +@codeblock{ +#lang pollen +Hello world +} Click the @onscreen{Run} button. In the interactions window, you'll see the result: -@nested[#:style 'code-inset]{@racketvalfont{Hello world}} +@repl-output{Hello world} Not bad. I think Pollen just won the @link["http://en.wikipedia.org/wiki/List_of_Hello_world_program_examples"]{Hello World Tournament}. @@ -33,18 +39,23 @@ You can work with Pollen source files in any text editor. The key advantage of D Try editing your source file: -@racketmod[pollen +@codeblock{ +#lang pollen Goodbye Stranger Breakfast in America -Take the Long Way Home] +Take the Long Way Home +} You don't have to use Supertramp song titles. Any text will do. When you click @onscreen{Run} again, you'll see whatever you typed: -@nested[#:style 'code-inset]{@racketvalfont{Goodbye Stranger}@(linebreak)@racketvalfont{Breakfast in America}@(linebreak)@racketvalfont{Take the Long Way Home}} +@repl-output{Goodbye Stranger +Breakfast in America +Take the Long Way Home +} We won't do it a third time. You get the point — any plain text is valid within a Pollen source file, and gets printed as is. You never have to perform the incantations of typical programming languages: -@verbatim{ +@terminal{ print "Hello world" document.write('Hello world'); printf("Hello world"); @@ -59,13 +70,13 @@ Save this file with the name @tt{hello.txt.pp} in any convenient directory. The Open a terminal window and issue two commands: -@verbatim{ +@terminal{ > cd [directory containing your file] > raco pollen render hello.txt.pp} After a moment, a new file will appear called @tt{hello.txt}. Let's see what's in it: -@verbatim{ +@terminal{ > cat hello.txt Goodbye Stranger Breakfast in America @@ -96,13 +107,13 @@ You've just learned two ways to see the output of a Pollen source file — firs Now here's a third: the Pollen project server. Here's how you start it. Return to your terminal window and issue two commands: -@verbatim{ +@terminal{ > cd [directory containing your hello.txt.pp file] > raco pollen start} After a moment, you'll see the startup message: -@verbatim{ +@terminal{ Welcome to Pollen 0.001 (Racket 6.0.0.5) Project root is /path/to/your/directory Project server is http://localhost:8080 (Ctrl-C to exit) @@ -113,7 +124,7 @@ Open a web browser and point it at @link-tt{http://localhost:8080/index.ptree}. Among them will be @tt{hello.txt}, with a greyed-out @tt{.pp} extension. Click on it, and you'll be taken to @link-tt{http://localhost:8080/hello.txt}, where you'll see: -@verbatim{ +@terminal{ Goodbye Stranger Breakfast in America Take the Long Way Home @@ -121,14 +132,17 @@ Take the Long Way Home That's the boring part. Here's the good part. Leave the project server running. Open your source file again in DrRacket and edit it as follows: -@racketmod[#:file "hello.txt.pp" pollen +@fileblock["hello.txt.pp" +@codeblock{ +#lang pollen Mean Street Panama -Hear About It Later] +Hear About It Later +}] Go back to your web browser and reload @link-tt{http://localhost:8080/hello.txt}. Now you'll see this: -@verbatim{ +@terminal{ Mean Street Panama Hear About It Later} @@ -148,10 +162,13 @@ A @italic{preprocessor} is a tool for making systematic, automated changes to a For instance, HTML. In DrRacket, create a new file called @tt{margin.html.pp} in your project directory: -@racketmod[#:file "margin.html.pp" pollen +@fileblock["margin.html.pp" +@codeblock{ +#lang pollen 5em is the inset. -] + +}] The ``@tt{.pp}'' file extension — which you saw before, with @tt{hello.txt.pp} — stands for ``Pollen preprocessor.'' You can use the Pollen preprocessor with any text-based file by inserting @tt{#lang pollen} as the first line, and adding the @tt{.pp} file extension. @@ -159,12 +176,14 @@ But for now, go to your @link["http://localhost:8080/index.ptree"]{project dashb Let's suppose you want to change the inset to 30%. Without a preprocessor, you'd have to search & replace each value. But with a preprocessor, you can move the inset value into a variable, and update it from that one location. So first, introduce a variable called @tt{my-inset} by using the @racket[define] command: -@racketmod[#:file "margin.html.pp" pollen +@fileblock["margin.html.pp" +@codeblock{ +#lang pollen ◊define[my-inset]{30%} 10em is the inset. -] +}] The ◊ character is called a @italic{lozenge}. In Pollen, the lozenge is a special character that marks anything Pollen should interpret as a command (rather than plain text). @@ -177,12 +196,14 @@ The whole command @tt{◊define[my-inset]{30%}} means ``create a variable called Then put the variable into the HTML like so, this time using the ◊ character with the variable name in the two places the value appears: -@racketmod[#:file "margin.html.pp" pollen +@fileblock["margin.html.pp" +@codeblock{ +#lang pollen ◊define[my-inset]{30%} ◊my-inset is the inset. -] +}] Now reload @link["http://localhost:8080/margin.html"]{@tt{margin.html}}. You'll see that the size of the margin has changed (because of the change to the @tt{style} attribute) and so has the text of the HTML. If you like, try editing @tt{my-inset} with different values and reloading the page. You can also try using @racket[define] to create another variable (for instance, to change the color of the box border). @@ -196,7 +217,9 @@ For instance, Markdown mode. Markdown is a simplified @link["https://daringfireb Try it. In DrRacket, create a file with the following lines and save it as @tt{downtown.html.pmd}: -@racketmod[#:file "downtown.html.pmd" pollen +@fileblock["downtown.html.pmd" +@codeblock{ +#lang pollen Pollen + Markdown ----------------- @@ -204,7 +227,7 @@ Pollen + Markdown + You **wanted** it — you #,(racketfont "_got_") it. + [search for Racket](https://google.com/search?q=racket) -] +}] As before, go to the @link["http://localhost:8080/index.ptree"]{dashboard} for the project server. This time, click the link for @link["http://localhost:8080/downtown.html"]{@tt{downtown.html}}. You'll see something like this: @@ -222,7 +245,9 @@ As usual, you're welcome to edit @tt{downtown.html.pmd} and then refresh the web In Markdown mode, you can still embed Pollen commands within the source as you did in preprocessor mode. Just keep in mind that your commands need to produce valid Markdown (as opposed to raw HTML). For instance, use @tt{define} to create a variable called @tt{metal}, and insert it into the Markdown: -@racketmod[#:file "downtown.html.pmd" pollen +@fileblock["downtown.html.pmd" +@codeblock{ +#lang pollen ◊define[metal]{Plutonium} Pollen + ◊metal @@ -231,7 +256,7 @@ Pollen + ◊metal + You **wanted** ◊metal — you #,(racketfont "_got_") it. + [search for ◊metal](https://google.com/search?q=◊metal) -] +}] Refresh @link["http://localhost:8080/downtown.html"]{@tt{downtown.html}} in the browser: @@ -260,17 +285,16 @@ Let's convert our Markdown example into Pollen markup. Marking up content is sim -@racketmod[#:file "uptown.html.pm" pollen +@fileblock["uptown.html.pm" +@codeblock{ +#lang pollen ◊headline{Pollen markup} ◊items{ - -◊item{You ◊strong{wanted} it — you ◊em{got} it.} - -◊item{◊link["https://google.com/search?q=racket"]{search for Racket}} - -}] + ◊item{You ◊strong{wanted} it — you ◊em{got} it.} + ◊item{◊link["https://google.com/search?q=racket"]{search for Racket}} +}}] Go to the @link["http://localhost:8080/index.ptree"]{project dashboard} and click on @link["http://localhost:8080/uptown.html"]{@tt{uptown.html}}. You'll see something like this: @@ -281,16 +305,19 @@ That's not right. What happened? We marked up the source using a combination of standard HTML tags (@tt{strong}, @tt{em}) and nonstandard ones (@tt{headline}, @tt{items}, @tt{item}, @tt{link}). This is valid Pollen markup. (In fact, if you look at @link["http://localhost:8080/out/markup.html"]{the generated source}, you'll see that they didn't disappear.) But since we're targeting HTML, we need to convert our custom tags into valid HTML tags. -For that, we'll make a special file called @tt{project-require.rkt}. This is a file in the standard Racket language that provides helper functions to decode the source. The definitions won't make sense yet. But this is the quick tour, so all you need to do is copy, paste, and save: +For that, we'll make a special file called @tt{directory-require.rkt}. This is a file in the standard Racket language that provides helper functions to decode the source. The definitions won't make sense yet. But this is the quick tour, so all you need to do is copy, paste, and save: + +@fileblock["directory-require.rkt" +@codeblock{ +#lang racket/base -@racketmod[#:file "project-require.rkt" racket/base (require pollen/tag) (provide (all-defined-out)) -(define headline (make-tag-function 'h2)) -(define items (make-tag-function 'ul)) -(define item (make-tag-function 'li 'p)) +(define headline (make-default-tag-function 'h2)) +(define items (make-default-tag-function 'ul)) +(define item (make-default-tag-function 'li 'p)) (define (link url text) `(a [[href ,url]] ,text)) -] +}] Return to the @link["http://localhost:8080/index.ptree"]{project dashboard} and click on @link["http://localhost:8080/uptown.html"]{@tt{uptown.html}}. Now you'll get the right result: @@ -316,7 +343,9 @@ When it needs a template, Pollen first looks for a file in the project directory So let's create @tt{template.html}. Make a new file that with the following lines and save it to the same directory as @tt{uptown.html.pm}: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock{ +
    @@ -324,7 +353,7 @@ This file is ◊here
    ◊->html{◊doc}
    -}} +}] This is a simple HTML file that should look familiar, except for the two template variables. The first, @tt{here}, contains the name of the current source file. As before, the lozenge character marks it as a Pollen command rather than text, so you write it as @tt{◊here}. The other command, @tt{◊->html{◊doc}}, takes the content from the source file, which is contained in a variable called @tt{doc}, and converts it to HTML with a Pollen function called @tt{->html}. @@ -334,7 +363,7 @@ Go back to your web browser and reload @link["http://localhost:8080/uptown.html" Pollen can also be used as a dynamic preview server for Scribble files. From your terminal, do the following: -@verbatim{ +@terminal{ > cd [directory containing your Scribble files] > raco pollen start} diff --git a/scribblings/tag.scrbl b/scribblings/tag.scrbl index 5de973a..4d20be4 100644 --- a/scribblings/tag.scrbl +++ b/scribblings/tag.scrbl @@ -13,14 +13,14 @@ Convenience functions for working with tags. @defproc[ -(make-tag-function +(make-default-tag-function [id txexpr-tag?]) (-> txexpr?)] -Make a tag function for @racket[_id]. As arguments, a tag function takes an optional set of X-expression attributes (@racket[txexpr-attrs?]) followed by X-expression elements (@racket[txexpr-elements?]). From these, the tag function creates a tagged X-expression using @racket[_id] as the tag. +Make a default tag function for @racket[_id]. As arguments, a tag function takes an optional set of X-expression attributes (@racket[txexpr-attrs?]) followed by X-expression elements (@racket[txexpr-elements?]). From these, the tag function creates a tagged X-expression using @racket[_id] as the tag. @examples[ (require pollen/tag) -(define beaucoup (make-tag-function 'em)) +(define beaucoup (make-default-tag-function 'em)) (beaucoup "Bonjour") (beaucoup '((id "greeting")) "Bonjour") ] @@ -29,7 +29,7 @@ Entering attributes this way can be cumbersome. So for convenience, a tag functi @examples[ (require pollen/tag) -(define beaucoup (make-tag-function 'em)) +(define beaucoup (make-default-tag-function 'em)) (beaucoup 'id: "greeting" 'class: "large" "Bonjour") (code:comment @#,t{Don't forget the colons}) (beaucoup 'id "greeting" 'class "large" "Bonjour") @@ -38,3 +38,19 @@ Entering attributes this way can be cumbersome. So for convenience, a tag functi ] Pollen also uses this function to provide the default behavior for undefined tags. See @racket[#%top]. + +@defproc[ +(split-attributes +[parts list?]) +(values txexpr-attrs? txexpr-elements?)] +Helper function for custom tag functions. Take a rest argument that possibly includes tag attributes plus elements, and split it into attributes and elements. If there are no attributes, that return value will be the empty list. Properly parses the abbreviated Pollen syntax for attributes (described in @racket[make-default-tag-function]). + +@examples[ +(require pollen/tag) +(define (tag . parts) + (define-values (attrs elements) (split-attributes parts)) + (values attrs elements)) +(tag "Hello world") +(tag '((key "value")) "Hello world") +(tag 'key: "value" "Hello world") +] \ No newline at end of file diff --git a/scribblings/top.scrbl b/scribblings/top.scrbl index f3c2d0a..882d385 100644 --- a/scribblings/top.scrbl +++ b/scribblings/top.scrbl @@ -25,7 +25,7 @@ In standard Racket, @racket[#%top] is the function of last resort, called when @ In the Pollen markup environment, however, this behavior is annoying. Because when you're writing X-expressions, you don't necessarily want to define all your tags ahead of time. -So Pollen redefines @racket[#%top]. For convenience, Pollen's version of @racket[#%top] assumes that an undefined tag should just refer to an X-expression beginning with that tag (and uses @racket[make-tag-function] to provide this behavior): +So Pollen redefines @racket[#%top]. For convenience, Pollen's version of @racket[#%top] assumes that an undefined tag should just refer to an X-expression beginning with that tag (and uses @racket[make-default-tag-function] to provide this behavior): @examples[ (code:comment @#,t{Again, let's call em without defining it, but using pollen/top}) diff --git a/scribblings/tutorial-first.scrbl b/scribblings/tutorial-first.scrbl index 7932cfc..e73122a 100644 --- a/scribblings/tutorial-first.scrbl +++ b/scribblings/tutorial-first.scrbl @@ -1,6 +1,6 @@ #lang scribble/manual -@(require (for-label pollen/world)) +@(require (for-label pollen/world) racket/runtime-path "mb-tools.rkt") @title[#:tag "first-tutorial"]{First tutorial} @@ -56,11 +56,13 @@ DrRacket is the IDE for the Racket programming language, and other languages mad Launch DrRacket. Start a new file. The code in the file will look like this: -@racketmod[racket] +@codeblock{ +#lang racket +} Within the main window, you should also see an @italic{interactions window}, which shows the output of the current file, and starts out looking something like this (details, like the version number, will vary): -@verbatim{ +@terminal{ Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m]. Language: racket; memory limit: 1000 MB. > } @@ -72,15 +74,17 @@ If you don't see the interactions window, select @menuitem["View" The first line of every Racket source file, and every Pollen source file, is called the @italic{@racketfont{#lang} line}. The @racketfont{#lang} line identifies the language used to interpret the rest of the file. -@margin-note{For more about the @racketfont{#lang} line, see @secref[#:doc '(lib "scribblings/guide/guide.scrbl") "hash-lang"].} +@margin-note{For more about the @racketfont{#lang} line, see @secref["hash-languages" #:doc '(lib "scribblings/guide/guide.scrbl")]. BTW, it's pronounced @italic{hash lang}.} When you start a new Pollen source file in DrRacket, you'll need to change the @racketfont{#lang} line to the Pollen language. The simplest way is to change the first line to this: -@racketmod[pollen] +@codeblock{ +#lang pollen +} Now run your file by clicking the @onscreen["Run"] button in the upper-right corner, or select @menuitem["Racket" "Run"] from the menu. You'll get something like: -@verbatim{ +@terminal{ Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m]. Language: pollen; memory limit: 1000 MB. > @@ -93,8 +97,8 @@ Notice that the language is now reported as @racketfont{pollen}. If you like, ch Then click @onscreen["Run"] again. DrRacket will print an error: -@verbatim{@racketerror{Module Language: invalid module text -@(linebreak)standard-module-name-resolver: collection not found ...}} +@errorblock{Module Language: invalid module text +standard-module-name-resolver: collection not found ...} Why? Because there's no language called @racketfont{pollenxyz}. Switch it back to @racketfont{pollen} and let's move on. @@ -102,7 +106,7 @@ Why? Because there's no language called @racketfont{pollenxyz}. Switch it back t Here's a short, bad poem I wrote about CSS. -@verbatim{ +@terminal{ The margin is 42em. The border is red. The padding is 15em. @@ -111,26 +115,28 @@ The border is too. Paste the text of this poem into your DrRacket editing window, below the @racketfont{#lang} line, so it looks like this: -@nested[#:style 'code-inset]{@verbatim{ +@codeblock{ #lang pollen The margin is 42em. The border is red. The padding is 15em. -The border is too.}} +The border is too.} @onscreen["Run"] the file again. In the interactions window, you'll see: -@racketvalfont{ +@repl-output{ The margin is 8em. -@(linebreak)The border is blue. -@(linebreak)The padding is 2em. -@(linebreak)The border is too.} +The border is blue. +The padding is 2em. +The border is too.} This shows you something important: by default, any plain text in a Pollen source file is simply printed as written when you @onscreen["Run"] the file (minus the @racketfont{#lang} line, which is just for Racket's benefit). If you like, edit the text of the poem and click @onscreen["Run"] again. You'll see the updated text printed in the interactions window. @subsection{Saving & naming your source file} +@(noskip-note) + File naming in Pollen is consequential. Ultimately, every Pollen source file in your project will be @italic{rendered} into an output file. Each Pollen source file corresponds to one output file. @bold{The name of this output file will be the name of the source file minus the Pollen source extension.} So a source file called @racketfont{file.txt.pp} will become @racketfont{file.txt}. @@ -147,20 +153,22 @@ the output name @racketfont{poem.html} + the source extension @racketfont{.pp} = In a convenient location (e.g., your home directory or the desktop) create a new directory for your project called @racketfont{tutorial}. In this new directory, save your DrRacket file as @racketfont{poem.html.pp}. -@filebox["/path/to/tutorial/poem.html.pp"]{@verbatim{ +@fileblock["/path/to/tutorial/poem.html.pp" @codeblock{ #lang pollen The margin is 42em. The border is red. The padding is 15em. -The border is too.}} +The border is too. +}] @section{Using the project server} The project server is a web server built into Pollen. Just as DrRacket lets you run individual files and see if they work as you expect, the project server lets you preview and test your project as a real website. While working on your Pollen project, you may find it convenient to have DrRacket open on half your screen, and on the other half, a web browser pointing at the project server. -@image["scribblings/project-server.png" #:scale 0.7] +@(define-runtime-path project-server "project-server.png") +@image[project-server #:scale 0.7] ``Why can't I just open the HTML files directly in my browser?'' If you want to keep making web pages the way we did in 1996, go ahead. But that approach has several shortcomings. First, when you open files directly in your browser, you're cruising the local filesystem, and absolute URLs (the kind that start with a @litchar{/}) won't work. Second, if you want to test your website on devices other than your own machine — well, you can't. Third, you have to render your HTML files in advance, whereas the project server is clever about doing this dynamically. @@ -176,13 +184,13 @@ Before we start the project server, a word about the @racketfont{raco pollen} co When you installed Racket, Racket installed a utility program called @racketfont{raco}. This name is short for @bold{Ra}cket @bold{co}mmand, and @racketfont{raco} acts as a hub for — you guessed it — Racket commands. You used it when you first installed Pollen: -@verbatim{ +@terminal{ > raco pkg install pollen } The first argument after @racketfont{raco} is the subcommand. For instance, @racketfont{raco pkg ...} lets you install, update, and remove packages like so: -@verbatim{ +@terminal{ > raco pkg update pollen > raco pkg remove pollen } @@ -191,7 +199,7 @@ Likewise, @racketfont{raco pollen} lets you issue commands relevant to Pollen, l Now we'll start the project server. Go to your command line and enter the following: -@verbatim{ +@terminal{ > cd /path/to/tutorial > raco pollen start} @@ -199,7 +207,7 @@ Now we'll start the project server. Go to your command line and enter the follow After a moment, you'll see a startup message like this: -@verbatim{ +@terminal{ Welcome to Pollen 0.001 (Racket 6.x.x.x) Project root is /path/to/tutorial Project server is http://localhost:8080 (Ctrl-C to exit) @@ -210,7 +218,7 @@ Ready to rock} If you use the bare command @racketfont{raco pollen start}, the project server will start in the current directory. But if you want to start the project server elsewhere, you can add that directory as an argument like this: -@verbatim{ +@terminal{ > raco pollen start /some/other/path } @@ -220,20 +228,21 @@ If you want to access the project server from a different machine, you can't use Though port @racketfont{8080} is the default, you can start the project server on any port you like by adding it as an argument to @racketfont{raco pollen start}: -@verbatim{ +@terminal{ > raco pollen start /path/to/tutorial > raco pollen start /path/to/tutorial 8088 } @margin-note{You can also change the default port by altering @racket[world:default-port], or parameterizing it with @racket[world:current-server-port].} -Note that when you pass a port argument, you also have to pass a path argument. 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: -@verbatim{ +@terminal{ > cd /path/to/tutorial > raco pollen start 8088 @racketerror{/path/to/tutorial/8088 is not a directory} > raco pollen start . 8088 +Welcome to Pollen 0.001 (Racket 6.x.x.x) ... } @margin-note{You can run multiple project servers simultaneously. Just start them on different ports so they don't conflict with each other.} @@ -253,7 +262,7 @@ Thus, @racketfont{index.ptree}. The @racketfont{.ptree} extension is short for @ Let's look at the root-level dashboard for our project. First, make sure your project server is running: -@verbatim{ +@terminal{ > cd /path/to/tutorial > raco pollen start } @@ -262,7 +271,8 @@ Then, in your web browser, visit @link["http://localhost:8080/index.ptree"]{@rac You should see something like this: -@image["scribblings/dashboard.png" #:scale 1] +@(define-runtime-path dashboard "dashboard.png") +@image[dashboard #:scale 1] The top line tells us that we're in the root directory of the project. We didn't make an explicit @racketfont{index.ptree} file, so the project server just shows us a directory listing. @@ -273,7 +283,7 @@ We see the only file, @racketfont{poem.html.pp}. Note that the @racketfont{.pp} Every source-file entry in the dashboard has three links. The first link is attached to the filename itself, and takes you to a preview of the output file. If the output file doesn't yet exist — as is the case here — it will be dynamically rendered. (This is true whether you click its name in the dashboard, or link to it from another page.) So click the filename. You'll see in your web browser: -@nested[#:style 'code-inset]{ +@browser{ The margin is 42em. The border is red. The padding is 15em. The border is too.} Granted, this is a boring web page. The main point here is that you're seeing the @italic{output} from your source file, which didn't exist before. Notice that the address bar says @racketfont{http://localhost:8080/poem.html}, not @racketfont{poem.html.pp}. And if you look in your @racketfont{tutorial} directory, you'll see a new file called @racketfont{poem.html}. @@ -284,17 +294,18 @@ If you go back to the dashboard and click on the filename link again, you'll see But if you like, open your @racketfont{poem.html.pp} source file in DrRacket, edit the first two lines, and save the file: -@nested[#:style 'code-inset]{@verbatim{ +@codeblock{ #lang pollen The cave is pitch black. Look out for the grue. The padding is 15em. -The border is too.}} +The border is too. +} Go back to the dashboard and click on the filename. This time, you'll see: -@nested[#:style 'code-inset]{ +@browser{ The cave is pitch black. Look out for the grue. The padding is 15em. The border is too.} Here, Pollen notices that the source file has changed, so it refreshes the output file. This makes it convenient to work between DrRacket and your web browser, editing source and then reloading to see the changes. @@ -303,21 +314,22 @@ The other two links in the dashboard are labeled @racketfont{in} and @racketfont The link labeled @racketfont{in} will display the contents of the source file: -@nested[#:style 'code-inset]{@verbatim{ +@codeblock{ #lang pollen The cave is pitch black. Look out for the grue. The padding is 15em. -The border is too.}} +The border is too. +} The link labeled @racketfont{out} will display the contents of the output file (just like the ``view source'' option in your web browser): -@nested[#:style 'code-inset]{@verbatim{ +@terminal{ The cave is pitch black. Look out for the grue. The padding is 15em. -The border is too.}} +The border is too.} For now, the files are identical except for the @racketfont{#lang} line. But let's change that. @@ -343,27 +355,28 @@ The file extension of a Pollen source file tells Pollen what kind of processing ``The preprocessor be used with @bold{any} kind of text-based file?'' Right. ``But how?'' The preprocessor reads the source file, handles any Pollen commands it finds, and lets the rest of the content pass through untouched. To the preprocessor, it's all just text data. It doesn't care whether that text represents HTML, CSS, JavaScript, or even @link["https://en.wikipedia.org/wiki/TI-BASIC"]{TI-BASIC}. -Because the preprocessor only deals in text, the Pollen commands you use in the preprocessor also have to produce text. Moreover, Pollen doesn't enforce the semantics of the underlying file — that's your responsibility. For instance, Pollen won't stop you from doing nonsensical things like this: +One caveat: because the preprocessor only deals with text, the Pollen commands you use in the preprocessor also have to produce text. Moreover, Pollen doesn't enforce the syntax rules of the underlying file — that's your responsibility. For instance, Pollen won't stop you from doing nonsensical things like this: -@filebox["bad-poem.html.pp"]{@verbatim{ +@fileblock["bad-poem.html.pp" @codeblock{ #lang pollen The cave is pitch black. Look out for the grue. ◊(insert-mp3-recording-of-scream) - }} + }] -Here, the result is not going to be valid HTML, because you can't simply drop binary data in the middle of an HTML file. To paraphrase Mr. Babbage — garbage in, garbage out. +Pollen will fulfill your request, but the result won't be valid HTML, because you can't simply drop binary data in the middle of an HTML file. To paraphrase Mr. Babbage — garbage in, garbage out. I've encouraged you to mess with the source file, but let's return it to its original state: -@filebox["/path/to/tutorial/poem.html.pp"]{@verbatim{ +@fileblock["/path/to/tutorial/poem.html.pp" @codeblock{ #lang pollen The margin is 42em. The border is red. The padding is 15em. -The border is too.}} +The border is too. +}] This file has @racketfont{#lang pollen} as the first line, and @racketfont{.pp} as the file extension, so it meets the minimum requirements for the preprocessor. @@ -371,7 +384,7 @@ This file has @racketfont{#lang pollen} as the first line, and @racketfont{.pp} Let's update our source so it produces valid HTML. Edit the source as follows: -@filebox["/path/to/tutorial/poem.html.pp"]{@verbatim{ +@fileblock["/path/to/tutorial/poem.html.pp" @codeblock{ #lang pollen @@ -383,21 +396,21 @@ The padding is 15em. The border is too. -}} +}] Return to the project server and view @link["http://localhost:8080/poem.html" "http://localhost:8080/poem.html"]. Earlier, the output looked like this: -@nested[#:style 'code-inset]{ +@browser{ The margin is 42em. The border is red. The padding is 15em. The border is too.} But now, because of the @racketfont{
    } tag, the poem will appear in a monospaced font, and the line breaks will be preserved:
     
    -@nested[#:style 'code-inset]{
    -@tt{The margin is 42em.
    -@(linebreak)The border is red.
    -@(linebreak)The padding is 15em.
    -@(linebreak)The border is too.}}
    +@terminal{
    +The margin is 42em.
    +The border is red.
    +The padding is 15em.
    +The border is too.}
     
     As before, because the source has changed, Pollen refreshes the output file. From the dashboard, you can use the @racketfont{in} and @racketfont{out} links to inspect the source and output. 
     
    @@ -409,21 +422,23 @@ I mentioned that the preprocessor reads the file and handles any Pollen commands
     
     Pollen commands can be embedded in your source file using one of two modes: @italic{Racket mode} or @italic{text mode}. We'll try text mode in a later tutorial. For now, we'll use Racket mode.
     
    -To make a Racket-mode Pollen command, just take any Racket expression and put the lozenge character (@litchar["◊"]) in front of it. For instance, these are valid Racket expressions:
    +To make a Racket-mode Pollen command, just take any Racket expression and put the lozenge character @litchar["◊"] in front of it. For instance, these are valid Racket expressions:
     
    -@nested[#:style 'code-inset]{@verbatim{
    -    (define inner 2)
    -    (define edge (* inner 4))
    -    (define color "blue")
    -}}
    +@codeblock{
    +#lang racket
    +(define inner 2)
    +(define edge (* inner 4))
    +(define color "blue")
    +}
     
     And these are the equivalent commands in Pollen:
     
    -@nested[#:style 'code-inset]{@verbatim{
    -    ◊(define inner 2)
    -    ◊(define edge (* inner 4))
    -    ◊(define color "blue")
    -}}
    +@codeblock{
    +#lang pollen
    +◊(define inner 2)
    +◊(define edge (* inner 4))
    +◊(define color "blue")
    +}
     
     How to type a lozenge:
     @(linebreak)@bold{Mac}: option + shift + V
    @@ -432,7 +447,7 @@ How to type a lozenge:
     
     @subsection{Racket basics (if you're not familiar)}
     
    -``But how am I supposed to know Racket?'' You don't. So we'll start now. Here are the five basic rules of Racket:
    +``But I've never used Racket.'' Today, you start. Here are the five basic rules of Racket:
     
     @itemlist[#:style 'ordered
     
    @@ -452,14 +467,15 @@ How to type a lozenge:
     
     That should tell you enough to infer what's going on in the Pollen commands above:
     
    -@nested[#:style 'code-inset]{@verbatim{
    -    ◊(define inner 2)
    -    ◊; create a variable 'inner' that holds the value 2
    -    ◊(define edge (* inner 4))
    -    ◊; create a variable 'edge' that's four times the value of 'inner'
    -    ◊(define color "blue")
    -    ◊; create a variable 'color' that holds the value "blue"
    -}}
    +@codeblock{
    +#lang pollen
    +◊(define inner 2)
    +◊; create a variable 'inner' that holds the value 2
    +◊(define edge (* inner 4))
    +◊; create a variable 'edge' that's four times the value of 'inner'
    +◊(define color "blue")
    +◊; create a variable 'color' that holds the value "blue"
    +}
     
     To learn more about Racket syntax, consider a detour through the excellent @other-doc['(lib "scribblings/quick/quick.scrbl")].
     
    @@ -468,8 +484,9 @@ To learn more about Racket syntax, consider a detour through the excellent @othe
     
     Let's use commands to define variables that will hold some values for our page. First, add a @racketfont{} tag to your source file, and three commmands to define three variables:
     
    -@filebox["/path/to/tutorial/poem.html.pp"]{@verbatim{
    +@fileblock["/path/to/tutorial/poem.html.pp" @codeblock{
     #lang pollen
    +
     
     
     
    @@ -485,11 +502,11 @@ The padding is 15em.
     The border is too.
     
    -}} +}] Then look at @link["http://localhost:8080/poem.html" "http://localhost:8080/poem.html"] again. Does it look the same? Not a trick question — it should. If you click the @onscreen{Out} link on the dashboard, you'll see this: -@nested[#:style 'code-inset]{@verbatim{ +@terminal{ @@ -505,16 +522,17 @@ The padding is 15em. The border is too. -}} +} -What's happening here? Our @racketfont{◊(define ...)} commands just define variables, so they don't evaluate to any value. Instead, we get blank lines. So far, so good. +What's with the blank lines? Don't panic — our @racketfont{◊(define ...)} commands define variables, so they don't evaluate to any value. Instead, we get blank lines. So far, so good. @subsection{Inserting values from variables} To insert the value of a variable in our file, we use the command @litchar{◊|}@italic{variable-name}@litchar{|}. Let's do that now: -@filebox["/path/to/tutorial/poem.html.pp"]{@verbatim{ +@fileblock["/path/to/tutorial/poem.html.pp" @codeblock{ #lang pollen + @@ -530,15 +548,15 @@ The padding is ◊|inner|em. The border is too. -}} +}] Here, we're replacing three values in the poem with the variables containing those values — @racketfont{◊|edge|}, @racketfont{◊|color|}, and @racketfont{◊|inner|}. @link["http://localhost:8080/poem.html"]{Reload the file} in the project server, and you'll see: -@nested[#:style 'code-inset]{@verbatim{ +@terminal{ The margin is 8em. The border is blue. The padding is 2em. -The border is too.}} +The border is too.} Hey, look at that — the text of the poem changed. Now it even rhymes. @@ -551,8 +569,10 @@ Our poem makes claims about the @racketfont{margin}, @racketfont{border}, and @r Update the @racketfont{} section of the page with a new @racketfont{ ◊(->html doc) -}} +}] When you refresh @racketfont{article.html} in the project server, you'll see that the heading now has a gray background, and one word in the text is red. @@ -353,63 +397,80 @@ Let's suppose that we'd rather use the name of the article — @italic{Deep Tho Beyond that, we just need to know the tag name that contains the title. If we have a little Markdown expertise, we might already know that this part of our Markdown source: -@verbatim{Deep Thought - ============} + +@codeblock[#:keep-lang-line? #f]{ +#lang pollen +Deep Thought +============ +} is going to produce a tag named @racketfont{h1}. What if we don't have all the Markdown conversions memorized? No problem. We can still figure out the tag name by running the @racketfont{article.html.pmd} source file in DrRacket and looking at the X-expression that results: -@nested[#:style 'code-inset]{@racketvalfont{'(root (h1 ((id "my-article")) "Deep Thought") (p () "I am " -@(linebreak)(strong () "so") " happy to be writing this."))}} +@repl-output{'(root (h1 ((id "my-article")) "Deep Thought") (p () "I am " +(strong () "so") " happy to be writing this."))} Either way, now we know that the text @italic{Deep Thought} lives in the @racketfont{h1} tag. So we update our template accordingly (for brevity, I'm going to omit the @racketfont{style} tag in these examples, but it's fine to leave it in): -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊(select 'h1 doc) ◊(->html doc) -}} + +}] When you refresh the page in the project server, the page title will now appear as ``Deep Thought.'' Of course, you can also combine static and dynamic elements in your template, like so: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊(select 'h1 doc), by MB ◊(->html doc) -}} + +}] The page title will now be ``Deep Thought, by MB''. A couple notes on command syntax. We inserted the @racket[select] and @racket[->html] commands using Racket-mode syntax. We could also use text-mode syntax and write the commands this way: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊select['h1 doc], by MB ◊->html[doc] -}} + +}] This is exactly equivalent to the previous example. Skeptics are welcome to confirm this by checking the result in the project server. Finally, notice that in the @racket[select] command, the tag name @racket['h1] is written with a quote mark, whereas @racketfont{doc} is not. This is an easy place to get tripped up, but the rule is simple: you don't use a quote mark when you're referring to the name of an existing function or variable (like @racket[select] or @racketfont{doc}). But you do need a quote mark when you're using the text as a literal value. -@(require scribble/core) -@(define (racketfont* . args) - (element 'tt args)) -@margin-note{Racket (and hence Pollen) makes a distinction between @secref["symbols" #:doc '(lib "scribblings/guide/guide.scrbl")] (e.g. @racket['h1]) and @secref["strings" #:doc '(lib "scribblings/reference/reference.scrbl")] (e.g. @racket["h1"]). Without getting into the weeds, just note for now that the tag of an X-expression is always a symbol, not a string. But if you write @racketfont{◊(select "h1" doc)}, the command will still work, because Pollen will treat it as @racketfont*{◊(select 'h1 doc)}, consistent with a general policy of not being persnickety about input types when the intention is clear.} +@margin-note{Racket (and hence Pollen) makes a distinction between @secref["symbols" #:doc '(lib "scribblings/guide/guide.scrbl")] (e.g. @racket['h1]) and @secref["strings" #:doc '(lib "scribblings/reference/reference.scrbl")] (e.g. @racket["h1"]). Without getting into the weeds, just note for now that the tag of an X-expression is always a symbol, not a string. But if you write @racketfont*{◊(@racket[select] "h1" doc)}, the command will still work, because Pollen will treat it as @racketfont*{◊(@racket[select] 'h1 doc)}, consistent with a general policy of not being persnickety about input types when the intention is clear.} @subsection{Linking to an external CSS file} If you're a super web hotshot, you probably don't put your CSS selectors in the @racketfont{} tag. Instead, you link to an external CSS file. So it will not surprise you that in Pollen, you can do this by adding the usual @racketfont{} tag to your HTML template, in this case a file called @racketfont{styles.css}: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊select['h1 doc], by MB ◊->html[doc] -}} + +}] Fans of hand-coded CSS, I trust you to take it from here: make your @racketfont{styles.css} file, and enjoy the results. @@ -419,14 +480,17 @@ Yes, of course. Here's the rule of thumb: when you're making links between files So to use a dynamic CSS file, we don't need to make any changes to @racketfont{template.html}. We just need to add @racketfont{styles.css.pp} to the project directory: -@filebox["styles.css.pp"]{@verbatim{#lang pollen +@fileblock["styles.css.pp" +@codeblock{ +#lang pollen ◊(define h1-color "blue") ◊(define strong-color "green") body {padding: 3em; font-size: 20px;} h1 {background: ◊|h1-color|; color: white;} -strong {color: ◊|strong-color|;}}} +strong {color: ◊|strong-color|;} +}] Now, when you refresh @racketfont{article.html} in the project server, Pollen will generate the @racketfont{styles.css} file it needs, and you'll see the new colors in the page. As before, if you update @racketfont{styles.css.pp}, Pollen will notice and regenerate the CSS file when you refresh the page. @@ -460,31 +524,37 @@ If the multiple pages in your project are already ordered by filename, then you From earlier in the tutorial, you have a Markdown source file called @racketfont{article.html.pmd} that looks like this: -@filebox["article.html.pmd"]{@verbatim{ +@fileblock["article.html.pmd" +@codeblock{ #lang pollen Deep Thought ============ -I am **so** happy to be writing this.}} +I am **so** happy to be writing this. +}] Let's supplement this source file by creating two others for the project: -@filebox["barticle.html.pmd"]{@verbatim{ +@fileblock["barticle.html.pmd" +@codeblock{ #lang pollen Barticle Title ============== -The wonderful second part of the article.}} +The wonderful second part of the article. +}] -@filebox["carticle.html.pmd"]{@verbatim{ +@fileblock["carticle.html.pmd" +@codeblock{ #lang pollen Carticle Title ============== -The terrific third part.}} +The terrific third part. +}] As before, you can fill these source files with any sample Markdown content you like. Moreover, you don't have to use the filenames @racketfont{barticle.html.pmd} and @racketfont{carticle.html.pmd} — the point is that the intended sequence needs to match the alphabetic sorting of the filenames. @@ -511,20 +581,27 @@ To make any navigation link — up, down, sideways — the general idea is that First, let's just see @racketfont{here} on its own. Update your template as follows: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊select['h1 doc], by MB ◊->html[doc] The current page is called ◊|here|. -}} + +}] If you refresh @racketfont{article.html}, you will now see the line ``The current page is called article.html.'' Switch to @racketfont{barticle.html}, and you'll see ``The current page is called barticle.html.'' Makes sense, right? Now let's use pagetree functions to show the names of the previous and next pages. Consistent with the usual policy of obviousness, these functions are called @racket[previous] and @racket[next]: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊select['h1 doc], by MB @@ -533,13 +610,17 @@ The current page is called ◊|here|. The previous is ◊|(previous here)|. The next is ◊|(next here)|. -}} + +}] Refresh @racketfont{barticle.html}. You'll now see that ``The current page is called barticle.html. The previous is article.html. The next is carticle.html.'' So far, so good: we're correctly deriving the previous and next pages from the automatic pagetree. All that's left is to add hyperlinks, which is easy: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊select['h1 doc], by MB @@ -548,7 +629,8 @@ The current page is called ◊|here|. The previous is ◊|(previous here)|. The next is ◊|(next here)|. -}} + +}] Refresh @racketfont{barticle.html}, and you'll see that the names of the previous and next pages are now hyperlinks to those pages. Click through and convince yourself that it works. @@ -564,49 +646,64 @@ But since we have a whole programming language available in Pollen, that's a dul To handle @racketfont{article.html}, we want to hide the previous-page navigation link when there's no previous page. As it turns out, if the @racket[previous] function can't find a previous page, it will return false. So we just need to wrap our previous-page navigation in the @racket[when/block] command like so: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊select['h1 doc], by MB ◊->html[doc] The current page is called ◊|here|. -◊when/block[(previous here)]{The previous is ◊|(previous here)|.} +◊when/block[(previous here)]{The previous is +◊|(previous here)|.} The next is ◊|(next here)|. -}} + +}] -The basic structure of @racket[when/block] is @racketfont{◊when/block[@racketvarfont{condition}]{@racketvarfont{insert-this-text}}.} Note the square braces around the @racketvarfont{condition}, and the curly braces around the @racketvarfont{text}. Using @racketfont{(previous here)} as the condition is shorthand for ``when @racketfont{(previous here)} does not return false...'' +The basic structure of @racket[when/block] is @racketfont{◊when/block[@racketvarfont{condition}]{@racketvarfont{insert-this-text}}.} Note the square braces around the @racketvarfont{condition}, and the curly braces around the @racketvarfont{text}. Using @racket[(previous here)] as the condition is shorthand for ``when @racket{(previous here)} does not return false...'' -Programmers in the audience might be getting anxious about the repeated use of @racketfont{(previous here)} — you're welcome to store that value in a variable, and everything will work the same way: +Programmers in the audience might be getting anxious about the repeated use of @racket[(previous here)] — you're welcome to store that value in a variable, and everything will work the same way: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊select['h1 doc], by MB ◊->html[doc] The current page is called ◊|here|. ◊(define prev-page (previous here)) -◊when/block[prev-page]{The previous is ◊|prev-page|.} +◊when/block[prev-page]{The previous is +◊|prev-page|.} The next is ◊|(next here)|. -}} + +}] We need a different technique for handling the end of the next-page navigation, because we're not reaching the actual end of the pagetree. We're just reaching the end of the pages we care about navigating through. What condition will help us detect this? Here, we can notice that the names of our article pages all contain the string @racketfont{article}. While you'd probably want a more robust condition for a real project, in this tutorial, what we'll do is hide the next-page navigation if the name of the next page doesn't contain ``@racketfont{article}''. As we did before, we wrap our navigation line in the @racket[when/block] function: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊select['h1 doc], by MB ◊->html[doc] The current page is called ◊|here|. ◊(define prev-page (previous here)) -◊when/block[prev-page]{The previous is ◊|prev-page|.} +◊when/block[prev-page]{The previous is +◊|prev-page|.} ◊when/block[(regexp-match "article" (->string (next here)))]{ The next is ◊|(next here)|.} -}} + +}] This time, the condition is @racket[(regexp-match "article" (->string (next here)))]. How were you supposed to know this? You weren't. That's why this is a tutorial. Without going on a lengthy detour, the @racket[regexp-match] function returns true if the first string (in this case, @racket["article"]) is found inside the second string (in this case, we convert @racket[(next here)] to a string by wrapping it in @racket[->string]). @@ -620,29 +717,31 @@ Pagetree source files have a different syntax and status than other Pollen sourc So let's make an @racketfont{index.ptree} file. At its simplest, a pagetree file can just be a list of files in the intended order. In DrRacket, create a new file in your project directory as follows: -@filebox["index.ptree"]{@verbatim{ +@fileblock["index.ptree" +@codeblock{ #lang pollen carticle.html article.html barticle.html -}} +}] Now run the file. The result will be: -@nested[#:style 'code-inset]{@racketvalfont{'(pagetree-root carticle.html article.html barticle.html)}} +@repl-output{'(pagetree-root carticle.html article.html barticle.html)} Pretty boring, I know. But behind the scenes, Pollen's pagetree parser is making sure your tree is valid (e.g., no duplicate or malformed names). Today it's boring, but on the day you have a long and complicated pagetree, you will be grateful. Notice that the names in this pagetree are the names of @italic{output} files, not source files. This is deliberate, so that neither you nor Pollen has to care which files are static vs. dynamic. This next pagetree wouldn't be wrong in the sense of bad syntax — the pagetree parser won't complain — but it would be wrong in the sense of not-what-you-want, because it refers to source names rather than output names: -@filebox["bad-index.ptree"]{@verbatim{ +@fileblock["bad-index.ptree" +@codeblock{ #lang pollen carticle.html.pmd article.html.pmd barticle.html.pmd -}} +}] You also probably noticed that the files are in a different order than they were in the automatic pagetree: @racketfont{carticle.html} is first, followed by @racketfont{article.html} and then @racketfont{barticle.html}. This too is deliberate, so we can see what happens with a differently ordered pagetree. @@ -654,19 +753,24 @@ BAM! An error page with a yellow box that says @racketfont{Can’t convert #f to So let's go back and fix that. Because we don't have extraneous files in our pagetree anymore, we can change the second conditional in the template to work the same way as the first: -@filebox["template.html"]{@verbatim{ +@fileblock["template.html" +@codeblock[#:keep-lang-line? #f]{ +#lang pollen + ◊select['h1 doc], by MB ◊->html[doc] The current page is called ◊|here|. ◊(define prev-page (previous here)) -◊when/block[prev-page]{The previous is ◊|prev-page|.} +◊when/block[prev-page]{The previous is +◊|prev-page|.} ◊(define next-page (next here)) ◊when/block[next-page]{ The next is ◊|next-page|.} -}} + +}] Refresh @racketfont{barticle.html} — because you're updating the template, you don't need to restart the project server — and you'll see the right result. The previous-page link goes to @racketfont{article.html}, and the next-page link is hidden. diff --git a/scribblings/tutorial-third.scrbl b/scribblings/tutorial-third.scrbl index a8ac4da..faa6435 100644 --- a/scribblings/tutorial-third.scrbl +++ b/scribblings/tutorial-third.scrbl @@ -1,94 +1,736 @@ #lang scribble/manual -@(require (for-label pollen/world)) +@(require scribble/eval (for-label plot pollen/world pollen/tag racket/base pollen/template txexpr racket/list racket/string)) +@(require "mb-tools.rkt") -@title[#:tag "tutorial-3"]{Third tutorial} +@(define my-eval (make-base-eval)) +@(my-eval `(require pollen pollen/decode pollen/template pollen/tag xml racket/list txexpr)) -In this tutorial, you'll use Pollen to publish a multi-page article written in Markdown. You'll learn about: + +@title[#:tag "third-tutorial"]{Third tutorial} + +Now you're getting to the good stuff. In this tutorial, you'll use Pollen to publish a multi-page article written in Pollen markup. You'll learn about: + +@itemlist[ + + +@item{Adding tags & attributes with Pollen markup} + +@item{Attaching behavior to tag functions} + +@item{the @racketfont{directory-require.rkt} file} + +@item{@exec{raco pollen render} and @exec{raco pollen clone}} + + +] + +If you want the shortest possible introduction to Pollen, try the @secref["quick-tour"]. + +@section[#:tag-prefix "tutorial-3"]{Prerequisites} + +I'll assume you've completed the @secref["second-tutorial"] and that you understand the principles of Pollen authoring mode — creating source files, converting them to X-expressions, and then combining them with templates to make output files. + +Because now it's time to pick up the pace. You've learned how to do some handy things with Pollen. But we haven't yet exploited the full fusion of writing environment and programming language. I promised you that @secref["The_book_is_a_program" #:doc '(lib "pollen/scribblings/pollen.scrbl")], right? So let's do some programming. + + +@section{Pollen markup vs. XML} + +You can skip this section if XML holds no interest. But Pollen markup evolved out of my attempt to come up with an alternative to XML that would be more usable for writing. So if you're familiar with XML, the contrast may be helpful. + +@subsection{The XML problem} + +In the @secref["second-tutorial"], I made the case that Markdown is a limiting format for authors. Why? Markdown is essentially a notation system for HTML tags. As such, it has three problems: it's not semantic, it only covers a limited subset of HTML tags, and it can't be extended by an author. + +These problems are partly limitations of HTML itself. And these limitations were meant to be cured by XML — the @italic{X} stands for @italic{extensible}. In principle, XML allows you to define whatever tags you like and use them in your document. + +So why hasn't XML taken over the world? In practice, XML promises more than it delivers. The reasons are apparent to any writer who's attempted to use XML as an authoring format: @itemlist[ -@item{Sharing data between source files} +@item{@bold{Verbose syntax}. Unfortunately, XML relies on the same angle-bracket notation as HTML. If you think HTML source is hard to read, XML is even worse. Since much of writing involves reading, this feature is also a major bug.} + +@item{@bold{Validation overhead}. Integral to XML is the concept of @italic{validation}, which guarantees that a document meets certain formal criteria, usually asserted in a @italic{schema}. To get the full value from XML, you generally want to use validation. But doing so imposes a lot more work on you as an author, and removes much of the expressive potential of XML.} + +@item{@bold{Masochistic document processing}. I'm referring to XSLT, the preferred method of transforming XML documents. I know a little XSLT, so I'll concede that there's a method to its madness. But it's still madness.} + +] + +The nicest thing we could say about XML is that its intentions are good. It's oriented toward the right goals. But its benefits are buried under atrocious ergonomics. -@item{Meta and Doc} -@item(the project-require.rkt file) +@subsection{What Pollen markup does differently} -@item{Pagetrees} +Pollen markup can be seen as a way of reaping the benefits of XML without incurring the headaches. Like XML, Pollen markup allows you to freely tag your text. But unlike XML: + +@itemlist[ -@item{Pagetrees with raco pollen render} +@item{@bold{Simple syntax}. Pollen markup follows the usual conventions of Pollen commands.} +@item{@bold{No structural validation}. You can use any tags you want, in any order, and you needn't define them ahead of time. Your document will still work.} +@item{@bold{Racket processing}. Pollen markup tags can have behavior attached to them using Racket functions, either before you use them, or later.} ] -@subsection{Sharing data between preprocessor files} -The preprocessor is useful for inserting variables that hold values. But variables are only useful when they can be shared among multiple files. Let's look at one way to do that. +@subsection{``But I really need XML…''} -Any value in a Pollen file that's set up using @racket[define] can be pulled into another Pollen file using the @racket[require] function. For instance, let's set up another preprocessor file in the same directory as @racketfont["brennan.md.pp"], called @racketfont["dale.md.pp"]: +You can have XML. There's nothing wrong with using Pollen markup to generate XML files that can then be fed into an existing XML processing pipeline. In other words, using Pollen markup, you can treat XML as an output format rather than an input format. -@filebox["dale.md.pp"]{@verbatim{ +In this tutorial, I'll be rendering Pollen markup with an HTML template. But you could easily use the same workflow with an XML template and thus end up with XML files. + + +@section{Writing with Pollen markup} + +Pollen markup is a free-form markup system that lets you add arbitrary @italic{tags} and @italic{attributes} to your text. By arbitrary, I mean that they don't need to match up with an existing schema or specification (e.g., the tags permitted by HTML). They can — but that's an option, not a requirement. + +I like to think of Pollen markup a way of capturing not just the text, but also my @bold{ideas about the text}. Some of these are low-level ideas (``this text should be italicized''). Some are high-level ideas (``this text is the topic of the page''). Some are just notes to myself. In short, everything I know about the text becomes part of the text. + +In so doing, Pollen markup becomes the source code of the book. Let's try it out. + +@subsection{Creating a Pollen markup file} + +We're going to use Pollen markup to make a file that will ultimately be HTML. So consistent with the authoring-mode workflow we learned in the @secref["second-tutorial"], we'll start with our desired output filename, @racketfont{article.html}, and then append the Pollen markup suffix, @racketfont{.pm}. + +In DrRacket, start a new file called @racketfont{article.html.pm} like so (BTW you can use any sample text you like): + +@fileblock["article.html.pm" @codeblock{ #lang pollen + +I want to attend RacketCon this year. +}] -My name is _Dale_, and I enjoy: +Consistent with usual authoring-mode policy, when you run this file, you'll get an X-expression that starts with @racketvalfont{root}: -+ super-duper boring sauce +@repl-output{'(root "I want to attend RacketCon this year.")} -+ at least 3 fish nuggets}} +Remember, even though the first line of the file is @racketmodfont{#lang} @racketmodname[pollen] — same as the last tutorial — the new @racketfont{.pm} suffix signals that Pollen should interpret the source as Pollen markup. Look what happens if you goof up and put Markdown source in a Pollen markup file, like so: -In the project server, this will produce the expected output: +@codeblock{ +#lang pollen + +I am **so** excited to attend __RacketCon__ this year. +} -@nested[#:style 'code-inset]{@verbatim{ -My name is _Dale_, and I enjoy: +The Markdown syntax will be ignored, and pass through to the output: -+ super-duper boring sauce +@repl-output{'(root "I am **so** excited to attend __RacketCon__ this year.")} -+ at least 3 fish nuggets}} +Restore the non-Markdown source, and let's continue. -Now, let's update the content using values defined in @racketfont{brennan.md.pp}. We do this by adding the @racket[require] command to the top of our file: -@filebox["dale.md.pp"]{@verbatim{ +@subsection{Tags & tag functions} + +Pollen markup uses the same Pollen command syntax that we first saw in @secref["Adding_commands" #:doc '(lib "pollen/scribblings/pollen.scrbl")]. Previously, we used this command syntax to invoke functions like @racket[define] and @racket[->html]. Pollen markup is used to invoke a special kind of function called a @italic{tag function}, which is a function that, by default, adds a tag to the text. + +To see how this works, restore your @racketfont{article.html.pm} file to its original state: + +@codeblock{ +#lang pollen + +I want to attend RacketCon this year. +} + +We can add any tag with Pollen markup, but for now, let's start with an old favorite: @racketvalfont{em}, which is used in HTML to add emphasis to text. We apply a tag by starting with the lozenge character (◊) followed by the tag name @racketvalfont{em}, followed by the text in curly braces, like so: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +I want to attend ◊em{RacketCon this year}. +}] + +Run this file in DrRacket and see the X-expression that results: + +@repl-output{'(root "I want to attend " (em "RacketCon this year") ".")} + + +You won't be surprised to hear that you can nest tags: + +@fileblock["article.html.pm" @codeblock{ #lang pollen + +I want to attend ◊em{RacketCon ◊strong{this} year}.}] + +With the expected results: + +@repl-output{'(root "I want to attend " (em "RacketCon " (strong "this") " year") ".")} + +@subsection{Attributes} -◊(require "brennan.md.pp") +@italic{Attributes} are like tags for tags. Each attribute is a key–value pair where the key is any name, and the value is a string. Anyone who's seen HTML is familiar with them: -My name is _Dale_, and I enjoy: +@repl-output{Prof. Leonard} -+ super-duper boring sauce +Here, @racketvalfont{class} is an attribute for @racketvalfont{span} that has value @racketvalfont{"author"}. And this is what it looks like as an X-expression: -+ at least 3 fish nuggets}} +@repl-output{'(span ((class "author")) "Prof. Leonard")} -The three values that we defined in @racketfont{brennan.md.pp} — @racketfont{sauce-type}, @racketfont{nugget-type}, and @racketfont{nugget-quantity} — will now be available in @racketfont{dale.md.pp} under the same names, so we can insert them into the Markdown in the same way: +You can add any number of attributes to a tag (first as an X-expression, then as HTML): -@filebox["dale.md.pp"]{@verbatim{ +@repl-output{'(span ((class "author")(id "primary")(living "true")) "Prof. Leonard")} + +@repl-output{Prof. Leonard} + +In Pollen markup, attributes have the same logic, but a slightly different syntax. In keeping with the tag notation you just saw, the @racketvalfont{span} tag is added in the usual way: + +@fileblock["article.html.pm" @codeblock{ #lang pollen + +◊span{Prof. Leonard}}] + +Then you have two options for adding attributes. The verbose way corresponds to how the attributes appear in the X-expression: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊span['((class "author")(id "primary")(living "true"))]{Prof. Leonard} +}] + +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. + +This involves some superfluous typing, however, so Pollen also supports an abbreviated syntax for attributes: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊span['class:"author" 'id:"primary" 'living:"true"]{Prof. Leonard} +}] + +In this form, each attribute key starts with a quote mark @litchar{'} and ends with a colon @litchar{:}. As before, the attribute value is in quotation marks. + +Both of these forms will produce the same X-expression: + +@repl-output{'(span ((class "author")(id "primary")(living "true")) "Prof. Leonard")} + + +Now that you know how to make tags and attributes, you might wonder whether Pollen markup can be used as a quick & dirty HTML-notation system. Sure — for a quick & dirty project, why not. Recall that @secref["X-expressions" #:doc '(lib "pollen/scribblings/pollen.scrbl")] are just alternative notation for the standard angle-bracket notation used in HTML. So if you wanted HTML like this: + +@repl-output{
    Important News
    } + +You could write it in Pollen markup like so: + +@repl-output{◊div['class:"red" style:"font-size:150%"]{Important ◊em{News}}} + +And then just convert it (using the @racket[->html] function) into the HTML above. Thus, the tags you already know and love (?) can be used in Pollen markup, but with fewer keystrokes and cruft. + +Still, if Pollen markup were just an alternative notation system for HTML tags, it would be pretty boring. As I alluded above, that's merely a boring way to use it. + +In the XML spirit, Pollen markup lets you use any tags you want. That's considerably less boring. + +@subsection{What are custom tags good for?} + +XML jocks can skip this section, since you already know. But if you've been mired in Markdown or HTML, read on. + +Tags, broadly speaking, are a means of annotating a text with extra information, which I'll call @italic{metadata} (using that term in its generic sense, not in any fiddly computery way). Metadata is the key tool that enables an author to write a book with the benefits of @italic{semantic markup} and @italic{format independence}. -◊(require "brennan.md.pp") +@subsection{Semantic markup} -My name is _Dale_, and I enjoy: +@italic{Semantic markup} means adding metadata to text according to the meaning of the text, not merely its intended visual appearance. So rather than tagging @racketvalfont{RacketCon} with an @racketvalfont{em} tag, as we did above to indicate how the word should look, maybe we would tag it with an @racketvalfont{event} tag, to indicate what @italic{kind} of thing it is. -+ super-duper ◊sauce-type sauce +Semantic markup lets an author specify distinctions that would be ambiguous in pure visual terms, thereby capturing more meaning and intent. For instance, in books, italic styling is commonly applied to a number of unrelated types of information: emphasized words, movie titles, terms being used for the first time, headings, captions and labels, and so on. Under a non-semantic formatting scheme, perhaps one would tag them all @racketvalfont{em}. But in semantic terms, one would tag them @racketvalfont{movie-title}, @racketvalfont{first-use}, @racketvalfont{heading}, as appropriate. -+ at least ◊nugget-quantity ◊nugget-type nuggets}} +This has two major benefits. First, by separating appearance and meaning, an author can manage the content of the book in useful ways. For instance, if every movie title were tagged as @racketvalfont{movie-title} rather than @racketvalfont{italic}, then it would be simple to generate a list of all movies mentioned in the book (for the author's benefit) or a page index of movie references (for the reader's benefit). But without that semantic tagging, a movie title couldn't be distinguished from any other italicized text. -Reload the file in the project server, and you'll see the imported values: +@subsection{Format independence} -@nested[#:style 'code-inset]{@verbatim{ -My name is _Dale_, and I enjoy: +The second benefit of custom tags is @italic{format independence}, or the ability to change the rendering of the text to suit a particular device or context. -+ super-duper fancy sauce + When a text is encrusted with format-specific visual tags — for instance, HTML tags — then the document markup is entangled with a single output format. If you only need one output format, fine. -+ at least 12 chicken nuggets}} + But increasingly, book authors have been called upon to publish their work in multiple formats: paper and PDF, but also web, e-book, or other natively digital formats, that connect to devices with differing display capabilities. + + @margin-note{Yes, I know that many of these formats are based on variants of HTML. But the HTML you can use in a desktop web browser is quite different from, say, the HTML you can use in a Kindle .mobi file. The .mobi file has other technical requirements too, like an .ncx and .opf file. So despite some genetic kinship, these HTML-ish formats are best understood as separate targets.} + +Using a display-driven model to manage this complexity is a terrible idea — as anyone who's tried it can attest. Converting from one display-based file type to another — for instance, word processor to HTML, or HTML to PDF — is an exercise in frustration and drain-circling expectations. + +This isn't surprising. For a long time, text processing has been dominated by this display-driven model. Most word processors, like Microsoft Word and Pages, have been built around this model. It worked well enough in the era where most documents were eventually going to be printed on paper (or a paper simulator like PDF). HTML was a technical leap forward, but not a conceptual leap: it mostly represented the display options available in a web browser. + +@margin-note{There's a couple TeX fans at the back of the room, waving their arms. Yes, TeX got a lot of things right. In practice, however, it never became a core tool for electronic publishing (which, to be fair, didn't exist when TeX was written). Plenty of ideas in Pollen were lifted from TeX.} + +For a document to be format independent, two conditions have to be satisfied. + +First, the document has to be readable by other programs, so they can handle the conversion of format-independent markup into a format-specific rendering (e.g., mapping semantic tags like @racketvalfont{movie-title} onto visual tags like @racketvalfont{em}). Most word-processor formats, like Word's .docx, are bad for authoring because these formats are opaque and proprietary. We needn't get into the political objections. As a practical matter, they're inarguably restrictive — if you can't get your data out of your file, you're stuck. + +Second, the document itself has to be represented in a way that's independent of the particularities of any one format. For instance, HTML is a bad authoring format because it encourages authors to litter their text with HTML-isms like @racketvalfont{h1} and @racketvalfont{span}. These have no meaning outside of HTML, and thus will always cause conversion problems. The @seclink["Prelude__my_principled_objection_to_Markdown" + #:doc '(lib "pollen/scribblings/pollen.scrbl")]{same goes for Markdown}, which is simply HTML in disguise. + + + +The solution to the first condition is to use text-based markup rather than proprietary file types. The solution to the second condition is to let authors define custom tags for the document, rather than the other way around. Pollen markup incorporates both of these ideas. + + +@subsection{Using custom tags} + +You can insert a custom tag using the same syntax as any other tag. Suppose you want to use an @racketvalfont{event} tag to mark events. You would insert it like so: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +I want to attend ◊event{RacketCon} this year.}] + +This markup will turn into this X-expression: + +@repl-output{'(root "I want to attend " (event "RacketCon") " this year.")} + +Which is equivalent to this XML: + +@repl-output{I want to attend RacketCon this year.} + +In truth, Pollen doesn't notice any difference between a custom tag vs. a standard HTML tag vs. any other kind of tag. They're all just markup tags. If you want to restrict yourself to a certain vocabulary of tags, you can. If you want to set up Pollen to enforce those restrictions, you can do that too. But by default, Pollen doesn't impose restrictions like this. In general, you can pick any tag name you want, and it will work. + +Don't take my word for it. See what happens if you write this: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +I want to attend ◊verylongandimpracticaltagname{RacketCon} this year.}] + +One small but important exception to this rule. If you were wondering why I sometimes call them @italic{tag functions} instead of just @italic{tags}, it's because under the hood, every tag is implemented as a function. The default behavior of this function is just to wrap the text in a tag with the given name. + +The benefit of treating tags as functions will become evident later in this tutorial. But the cost of this approach is that tags occupy the same namespace as the other functions available in Pollen (and by extension, Racket). So if you try to use a tag name that's already the name of an existing function, an error will occur. + +For instance, let's suppose you try to use a custom tag called @racketvalfont{length}: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +The Panama Canal is ◊length{77km} across.}] + +When you run this file, you get an error: + +@errorblock{length: contract violation; +expected: list? +  given: "77km"} + +The problem is that Racket already provides a function called @racket[length]. Consistent with the usual rules of Pollen command notation, your command is interpreted as an attempt to invoke the @racket[length] function, rather than apply a tag named @racketvalfont{length}. + +In practice, namespace clashes are rare. But if necessary, they're easy to work around (for the simplest method, see @secref["Invoking_tag_functions" + #:doc '(lib "pollen/scribblings/pollen.scrbl")]). + + +@subsection{Choosing custom tags} + +You just saw that using custom tags is easy. Choosing custom tags, on the other hand, is less science than art. As the author, it's up to you. Some guidelines: + +@itemlist[ + + +@item{@bold{You're never doing it wrong.} I wanted to make sure you knew the case for semantic markup. But if your life would be easier just using HTML tags directly, go ahead.} + +@item{@bold{Tag iteratively.} Don't worry about getting all your tags right the first time through. Just as you write and then rewrite, add the tags that seem right now, and change or augment them later, because …} + +@item{@bold{Tags emerge from writing.} It's hopeless to try to specify all your tags in advance. As you write, you'll learn things about the text, which will suggest new tags.} + +@item{@bold{The best tag system is the one you'll stick with.} Tags aren't free. It takes effort to insert them consistently. Don't bother with an overambitious tag scheme that bores you more than it helps.} + +@item{@bold{For boilerplate, tags are faster than text.} If you find yourself repeatedly formatting certain text in a certain way — for instance, lists and tables — extract the content and wrap it in a tag that encapsulates the boilerplate.} + +] + +And most important: + +@itemlist[ + + +@item{@bold{Tags are functions.} As I @seclink["Tags___tag_functions" + #:doc '(lib "pollen/scribblings/pollen.scrbl")]{mentioned above}, every tag has a function behind it that uses the content of the tag as input. The default tag function just outputs the tag and its content. But you can replace this with any kind of function. So in practice, you can offload a lot of labor to tags. } +] + + + + As we'll see in the next section, this is where your book truly becomes programmable. + +@section{Tags are functions} + +@(noskip-note) + +If you've used HTML or XML, tags are just tags: things you type into the document that look the same going out as they did going in. Tags can be used to select document elements or assign styling (via CSS). But they don't have any deeper effect on the document content. + +That's not so in Pollen. Under the hood, Pollen is just an alternate way of writing code in the Racket programming language. And tags, instead of being inert markers, are actually functions. + +I think most of you know what a function is, but just to be safe — in programming, a @italic{function} is a chunk of code that accepts some input, processes it, and then returns a value. Asking a function to process some data is known as @italic{calling} the function. + +Leading us to the Three Golden Rules of Pollen Tags: + +@itemlist[#:style 'ordered + + +@item{@bold{Every Pollen tag calls a function with the same name.}} + +@item{@bold{The input values for that function are the attributes and content of the tag.}} + +@item{@bold{The whole tag — tag name, attributes, and content — is replaced with the return value of the called function.}} + +] + +You've already seen the simplest kind of function in a Pollen document: the @seclink["Tags___tag_functions" + #:doc '(lib "pollen/scribblings/pollen.scrbl")]{default tag function}, which emulates the behavior of standard markup tags. + + Let's revisit an earlier example, now with the help of the Golden Rules: + + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +I want to attend ◊em{RacketCon ◊strong{this} year}.}] + +What happens when you run this source? Working from the inside out, Pollen calls the function @racketvalfont{strong} with the input @racketvalfont{"this"}. The result is @racketvalfont{(strong "this")}. Then Pollen calls the function @racketvalfont{em} with the three input values @racketvalfont{"RacketCon " (strong "this") " year"}, which yields @racketvalfont{(em "RacketCon " (strong "this") " year")}. Finally, Pollen calls the @racketvalfont{root} function with everything in the document, resulting in: + +@repl-output{'(root "I want to attend " (em "RacketCon " (strong "this") " year") ".")} + +@subsection{Attaching behavior to tags} + +Sometimes this default behavior will suffice. But other times, you'll want to change the behavior of a tag. Why? Here are some useful examples of what you, as an author, can do with custom tag functions: + +@itemlist[ +@item{Automatically detect cross-references and add hyperlinks.} + +@item{Pull in data from an external source.} + +@item{Generate tables, figures, and other fiddly layout objects.} + +@item{Change content based on given conditions.} + +@item{Automatically detect line breaks, paragraphs, and lists.} + +@item{Insert boilerplate text.} + +@item{Anything annoying or repetitive.} + +@item{Mathematical computations.} + +@item{… and anything else you like to do with a programming language.} +] + +@margin-note{Having invited you to gaze across these vistas, I apologize that my example here in this tutorial is necessarily tip-of-the-iceberg. I'll be adding a more detailed guide to writing Pollen functions, both simple and crafty.} + +How do you change the behavior of a tag? By 1) writing a new function and 2) giving it the name of the tag. Once you do this, this new behavior will automatically be invoked when you use the tag. + +For example, let's redefine the @racketvalfont{strong} tag in our example above to simply print @racketvalfont{BOOM}: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊define[(strong . lines)]{BOOM} + +I want to attend ◊em{RacketCon ◊strong{this} year}}] + + +When you run this file, you indeed get: + +@repl-output{'(root "I want to attend " (em "RacketCon " "BOOM" " year"))} + +How does this work? First, although you can define a function in Pollen command syntax using either of @secref["The_two_command_modes__text_mode___Racket_mode" + #:doc '(lib "pollen/scribblings/pollen.scrbl")], it tends to be easier to use Racket mode. I wrote the first one in text mode. But for clarity, I'm going to switch to Racket mode (run this file and convince yourself it comes out the same): + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(define (strong word) "BOOM") + +I want to attend ◊em{RacketCon ◊strong{this} year}.}] + +Let's look at our new function definition. As usual, we start with the lozenge character (@litchar{◊}) to denote a Pollen command. Then we use @racket[define] to introduce a function definition. The name of the function comes next, which needs to match our tag name, @racketvalfont{strong}. The expression @racket[(strong word)] means ``the name of this function is @racket[strong], and it takes a single word as input, which we'll refer to as @racket[word].'' Finally we have the return value, which is @racket["BOOM"]. + +Let's run this file again, but go back to the Golden Rules to understand what happens. Working from the inside out, Pollen calls the function @racketvalfont{strong} with the input @racketvalfont{"this"} — same as before. But this time, the result of the @racket[strong] function is not @racketvalfont{(strong "this")}, but simply @racketvalfont{BOOM}. Then Pollen calls the function @racketvalfont{em} with the three input values @racketvalfont{"RacketCon " "BOOM" " year"}, which yields @racketvalfont{(em "RacketCon " "BOOM" " year")}. Finally, Pollen calls the @racketvalfont{root} function with everything in the document, resulting in: + +@repl-output{'(root "I want to attend " (em "RacketCon " "BOOM" " year"))} + +This example is contrived, of course. But the basic idea — defining a function with the name of a tag — is the foundation of programmability in Pollen. @bold{If you get this, and the Golden Rules, you get everything.} + +@subsection{Notes for experienced programmers} + +Having said that, some of you are probably eager to hack around a bit. Let me chip off a few more cubes from the iceberg to help you on your way. (Everyone else, take five.) + +@subsubsection{Point of no @code{return}} If you've written functions in other programming languages, you might be accustomed to using a @code{return} statement to send a value back from the function. This doesn't exist in Pollen or Racket — the return value of any function is just the last expression evaluated. In the example below, @racketvalfont{"BAP"} becomes the return value because it's in the last position, and @racketvalfont{"BOOM"} is ignored: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(define (strong word) "BOOM" "BAP") + +I want to attend ◊em{RacketCon ◊strong{this} year}.}] + +@subsubsection{Multiple input values & rest arguments} Sometimes a tag will have only one word or string that becomes its input. More likely, however, it will have multiple values (this is inevitable with nested tags, because the results aren't concatenated). For instance, if we attach our function to @racket[em] rather than @racket[strong]: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(define (em word) "BOOM") + +I want to attend ◊em{RacketCon ◊strong{this} year}.}] + +Look what happens: + +@errorblock{ +em: arity mismatch; +the expected number of arguments does not match the given number +expected: 1 +  given: 3 +} + +The error arises because the @racket[em] function is getting three arguments — @racketvalfont{"RacketCon " "BOOM" " year"} — but has been defined to only accept one argument, @racket[word]. This is the ``arity mismatch.'' + +To fix this, it's better to get in the habit of writing tag functions that accept an indefinite number of input values. You do this by defining your function with a @italic{@seclink["contracts-rest-args" #:doc '(lib "scribblings/guide/guide.scrbl")]{rest argument}} (as in, ``give me the rest of the input values.'') To use a rest argument, put it last in your list of input arguments, and add a period @litchar{.} before: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(define (em . parts) "BOOM") + +I want to attend ◊em{RacketCon ◊strong{this} year}.}] + +This time, the source file will run without an error, producing this: + +@repl-output{'(root "I want to attend " "BOOM" ".")} + +A rest argument like @racket[parts] is a @racket[list] of individual arguments. So if you want to unpack & process these arguments separately, you can use Racket's extensive list-processing functions (see @secref["pairs" #:doc '(lib "scribblings/guide/guide.scrbl")]). Also see @racket[quasiquote] below. + +@subsubsection{Returning an X-expression} Often, you won't use a tag function to replace a whole tag with a string — you'll replace it with a different tag, described by an X-expression, like so: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(define (em . parts) '(big "BOOM")) + +I want to attend ◊em{RacketCon ◊strong{this} year}.}] + +Which produces: + +@repl-output{'(root "I want to attend " (big "BOOM") ".")} + +The @racket[quote] mark @litchar{'} before the X-expression signals to Racket that you want to use what follows as a literal value. + +To build X-expressions that are more elaborate, you have two options. + +First is @racket[quasiquote]. Quasiquote works like quote, but starts with a backtick character @litchar{`}. What makes it ``quasi'' is that you can insert variables using the @racket[unquote] operator, which is a comma @litchar{,} or merge a list of values with the @racket[unquote-splicing] operator, which is a comma followed by an @"@" sign @litchar{,@"@"}. + +Let's adapt the example above to use @racket[quasiquote]. Suppose we want to take the @racket[parts] we get as input and put them inside a @racket[big] tag. This is easy to notate with @racket[quasiquote] and the @racket[unquote-splicing] operator, because @racket[parts] is a list: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(define (em . parts) `(big ,@"@"parts)) + +I want to attend ◊em{RacketCon ◊strong{this} year}.}] + +Which produces this: + +@repl-output{'(root "I want to attend " (big "RacketCon " (strong "this") " year") ".")} + +Of course you can also nest X-expressions in your return value: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(define (em . parts) `(extra (big ,@"@"parts))) + +I want to attend ◊em{RacketCon ◊strong{this} year}.}] + +The second option for building X-expressions is to use the @other-doc['(lib "txexpr/scribblings/txexpr.scrbl")] library that's included with Pollen (see those docs for more information). + +@subsubsection{Interpolating variables into strings} The usual way is to use the @racket[format] function: + +@racket[(format "String with variable: ~a" variable-name)] + +See the docs for @racket[format] and @racket[fprintf] for your options. + +Be careful if you're working with integers and X-expressions — a raw integer is treated as a character code, not an integer string. Using @racket[format] is essential: + +@examples[#:eval my-eval +(->html '(div "A raw integer indicates a character code: " 42)) +(->html `(div "Use format to make it a string: " ,(format "~a" 42))) +] + +@subsubsection{Parsing attributes} + +Detecting attributes in an argument list can be tricky because a) the tag may or may not have attributes, b) those attributes may be in standard or abbreviated syntax. For this reason, Pollen provides a @racket[split-attributes] function (in the @racket[pollen/tag] librar) that you can use in custom tag functions to separate the attributes and elements: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(require pollen/tag) + +◊(define (em . parts) + (define-values (attributes elements) (split-attributes parts)) + `(extra ,attributes (big ,@"@"elements))) + +I want to attend ◊em['key: "value"]{RacketCon}.}] + +This will move the @racket[elements] inside the @racket[big] tag, and attach the @racket[attributes] to the @racket[extra] tag: + +@repl-output{'(root "I want to attend " (extra ((key "value")) (big "RacketCon")) ".")} + + +@section[#:tag-prefix "tutorial-3"]{Intermission} + +That was a lot of heavy material. But it also covered the most essential idea in Pollen: that @bold{every tag is a function}. Congratulations on making it this far. + +The good news is that the rest of this tutorial will feel more relaxed, as we put these new principles to work. + +Sorry that this tutorial is longer than the others, but truly — this is the stuff that makes Pollen different. If you're not feeling enthusiastic by now, you should @link["http://www.buzzfeed.com/search?q=puppies"]{bail out}. + +Otherwise, get ready to rock. + +@section{Organizing functions} + +In the tag-function examples so far, we've defined each function within the source file where we used it. This is fine for quick little functions. + +But more often, you're going to want to use functions defined elsewhere, and store your own functions available so they're available to your source files. + +@margin-note{For now, we're just invoking functions within a Pollen markup file. But as you'll see in the fourth tutorial, any function can be called from any kind of Pollen source file.} + +@subsection{Using Racket's function libraries} + +Any function in Racket's extensive libraries can be called by loading the library with the @racket[require] command, which will make all its functions and constants available with the usual Pollen command syntax: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(require racket/math) + +Pi is close to ◊|pi|. +The hyperbolic sine of pi is close to ◊(sinh pi). +}] + +The result: + +@repl-output{'(root "Pi is close to " 3.141592653589793 "." "\n" "The hyperbolic sine of pi is close to " 11.548739357257748 ".")} + +One caveat — you're still in a Pollen markup file, so the return value of whatever function you call has to produce a string or an X-expression, so it can be merged into the document. @margin-note*{This is similar to the restriction introduced in the @seclink["Setting_up_a_preprocessor_source_file" + #:doc '(lib "pollen/scribblings/pollen.scrbl")]{first tutorial} where functions used in preprocessor files had to produce text.} +Pollen won't stop you from calling a function that returns an incompatible value, like @racket[plot], which returns a bitmap image: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(require math plot) + +Here's a sine wave: +◊(plot (function sin (- pi) pi #:label "y = sin(x)")) +}] + +But it won't work when you try to run it in DrRacket or load it in the project server. + +It would be fine, however, to call a different kind of @racket[plot] function that returned an SVG result, because any XML-ish data structure can be converted to an X-expression. + +@margin-note{Super web nerds also know that binary data can be converted into XML-ish form by encoding the file as a base-64 data URL — but if you know what I'm talking about, then you don't need my help to try it.} + +For functions that don't return a string or an X-expression, you can always make a conversion by hand. For instance, consider @racket[range], a Racket function that returns a list of integers: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(require racket/list) +A list of integers: ◊(range 5) +}] + +This will produce an error in DrRacket: + +@errorblock{ +pollen markup error: in '(root "A list of integers: " (0 1 2 3 4)), '(0 1 2 3 4) is not a valid element (must be txexpr, string, symbol, XML char, or cdata) +} + +In a case like this, you can explicitly convert the return value to a string (in whatever way makes sense): + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +◊(require racket/list racket/string) +A list of integers: ◊(string-join (map number->string (range 5))) +}] + +And get this output: + +@repl-output{'(root "A list of integers: " "0 1 2 3 4")} + + +@subsection[#:tag-prefix "tutorial-3"]{The @racketfont{directory-require.rkt} file} + +@(noskip-note) + +As you get more comfortable attaching behavior to tags using tag functions, you'll likely want to create some functions that can be shared between multiple source files. The @racketfont{directory-require.rkt} file is a special file that is automatically imported by Pollen source files in the same directory. So every function and value provided by @racketfont{directory-require.rkt} can be used in these Pollen files. + +First, using this file is not mandatory. You can always import functions and values from another file using @racket[require] (as seen in the previous section). The @racketfont{directory-require.rkt} is just meant to cure the tedium of importing the same file into every Pollen source file in your project. In a small project, not much tedium; in a large project, more. + +Second, notice from the @racketfont{.rkt} suffix that @racketfont{directory-require.rkt} is a source file containing Racket code, not Pollen code. This is the default because while Pollen is better for text-driven source files, Racket is better for code-driven source files. Still, the choice is yours: the name of this file can be changed by resetting the @racket[world:directory-require] value. + +Third, notice from the @racketfont{directory-} prefix that @racketfont{directory-require.rkt} is only used by Pollen source files @italic{in the same directory}. So if your project has source files nested inside a subdirectory, you'll need to explicitly create another @racketfont{directory-require.rkt} there and share the functions & values as needed. + +@margin-note{``Why not make this file visible throughout a project, rather than just a directory?'' Good idea, but I couldn't figure out how to do it without creating finicky new dependencies. If you have a better idea, I'm open to it.} + +Let's see how this works in practice. In the same directory as @racketfont{article.html.pm}, create a new @racketfont{directory-require.rkt} file as follows: + +@fileblock["directory-require.rkt" @codeblock{ +#lang racket +(define author "Trevor Goodchild") +(provide author) +}] + +Here we use the @racket[define] function (which we've seen before) to set @racket[author] equal to @racket["Trevor Goodchild"]. Note the final step: consistent with standard Racket rules, we have to explicitly @racket[provide] the new value so that other files can see it (unlike Python, things you @racket[define] in Racket are private by default, not public). + +Then update good old @racketfont{article.html.pm}: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen + +The author is ◊|author|. +}] + +Run this in DrRacket and you'll get: + +@repl-output{'(root "The author is " "Trevor Goodchild" ".")} + +Now, in the same dirctory, create a second Pollen source file: + +@fileblock["barticle.html.pm" @codeblock{ +#lang pollen + +The author is really ◊|author|? +}] + +Run this, and you'll get: + +@repl-output{'(root "The author is really " "Trevor Goodchild" "?")} + +That's all there is to it. Everything provided by @racketfont{directory-require.rkt} is automatically available within each Pollen source file. + +You can include functions, including tag functions, the same way. For instance, add a function for @racket[em]: + +@fileblock["directory-require.rkt" @codeblock{ +#lang racket +(define author "Trevor Goodchild") +(define (em . parts) `(extra (big ,@"@"parts))) +(provide author em) +}] + +Then use it in a source file: + +@fileblock["article.html.pm" @codeblock{ +#lang pollen -@margin-note{Those familiar with Racket know that Racket makes you explicitly @racket[provide] any variables you want to export. To make life simpler, Pollen inverts this behavior and automatically exports all defined symbols using @racket[(provide (all-defined-out))]. For more about the differences in behavior between Racket and Pollen, see @secref["File_formats" #:doc '(lib "pollen/scribblings/pollen.scrbl")].} +The ◊em{author} is ◊em{◊|author|}. +}] +With the expected results: +@repl-output{'(root "The " (extra (big "author")) " is " (extra (big "Trevor Goodchild")) ".")} +@;subsection{Importing from a Pollen source file} +@;subsection{Making a Racket package} +@section{Putting it all together} +[Coming soon] +@;section{ +1 directory-require.rkt +2-4 three source files +5 pagetree +6 template +} diff --git a/scribblings/world.scrbl b/scribblings/world.scrbl index e8666b3..89db7e4 100644 --- a/scribblings/world.scrbl +++ b/scribblings/world.scrbl @@ -25,11 +25,11 @@ A parameter that sets the HTTP port for the project server. Initialized to @rack )] The two exports from a compiled Pollen source file. Initialized to @racket['doc] and @racket['metas], respectively. -@(defthing world:project-require string?) -File implicitly required into every Pollen source file from its directory. Initialized to @racket["project-require.rkt"]. +@(defthing world:directory-require string?) +File implicitly required into every Pollen source file from its directory. Initialized to @racket["directory-require.rkt"]. -@defparam[world:check-project-requires-in-render? check? boolean?]{ -A parameter that determines whether the @racket[world:project-require] file is checked for changes on every pass through @racket[render]. (Can be faster to turn this off if you don't need it.) Initialized to @racket[#t].} +@defparam[world:check-directory-requires-in-render? check? boolean?]{ +A parameter that determines whether the @racket[world:directory-require] file is checked for changes on every pass through @racket[render]. (Can be faster to turn this off if you don't need it.) Initialized to @racket[#t].} @defthing[world:server-extras-dir string?] Name of directory where server support files live. Initialized to @racket["server-extras"].