Add @collection() shortcode

I wanted a more flexible way to show lists of related posts. The old way involved adding a collection block to a page’s front matter, like this:

[collection]
include = ["essays"]

This worked, but it had big limitations: you could only have one collection, and it always appeared at the bottom of the page. The new @collection() shortcode lets me drop collections (as many as I want) anywhere in any document, directly from the body of the document. I also added much improved sorting and filtering options.

So, TLDR, to show the 5 most recent quotes I’ve saved that are tagged favourite:

@collection(include="quotes" limit=5 filter="tags=favourite")

Which would render as:

My first effort had a circular dependency problem: how can you generate a page that needs a list of other pages, when those other pages might not have been generated yet? The solution is necessarily two-pass, and so while sharing its syntax with the other shortcodes, it does not run until later in the build.

  1. First Pass: When the site generator sees a @collection() shortcode, it doesn’t build the list right away. Instead, it leaves a unique placeholder and makes a note of the document and the parameters that were used.

  2. Second Pass: After all the pages have had their main content generated, a final step loops over the documents it made a note of. It now has all the data it needs to build the collection, so it renders the list into HTML and swaps it with the placeholder.

Despite being two pass, it’s pretty efficient as it only re-visits the pages that actually need it.

Collections exclude the current page unless the include_self=True boolean is passed, such as for a series table of contents.

Collections can contain documents that themselves contain collections, even wehn rendering the whole child document (e.g. style="body"). This is achieved through iterative.


Filtering:

Sorting & Slicing:

Display Styles:

Behavior:


This change completely removes the old front matter based collection system.