Layouts and Rendering
Currently we walk the tree of bales and render out the indexes and pages within them. Moving more of the knowledge of the tree structure into the render contex will hopefully simplify some of the job of rendering.
Legacy
In the old approach we had a seprataion between the Renderer
, and the
Renderable
. This abstracted over the index and standar pages. In our new
apprach pages are just pages. We still have some disconect betwen indexes and
standard pages however, see nav_prefix
.
Proposed Structure
When we walk a tree we initially create a root render context. This context contains just the title of documentaiton. We can also add more “global” state later, such as the global assets, and the highlighter.
When we render an item in a render context we then do one of two things:
- If we are rendering a plain page we create a
PageCtx
containing page specific information. - If we are rendering a bale we create a nested child
RenderCtx
. We can then create some specialPageCtx
to represent the index and render the index with that.
Render contexts should contain information abot the current bale, as well. This
allows navigation information to be quieried from the render context. This might
be either an Enum burried within the render cotnex, or a pair of types:
RenderState
, and RenderCtx
. In this approach the render context is the root
immutable information that should be used for all rendering, such as site title,
highlighter etc. The render state is then the bale-speciifc information. A
render state is what is then used for page layout and rendering.
The Layout Abstraction
Pages shoudl implement some trait that allows them to be quieried for items to
be used in the final rendered page. They shouldl not however know about the
final stucture of the page. Pages really only need to expose some <article>
which represents the page’s content, and some <ul>
that represents the TOC
tree.
With the page’s responsibilities tied down this way it should allow us to abstract away actual layout to some third item. I’m calling this the Layout. When a render happens we’ll need to provide some layout. This will probably live on the render ctx. Layouts are given access to a file that’s open for write, the current render state, and the renderable. A layout’s responsibility is solely that of putting HTML bits and bytes onto a page.
Libraries
Seprating rendering, layout, and renderables like this allows some portion of
docket
to be moved into a library. A client would then be able to create a
docket
from any directory, make a render_ctx
, and render to a target
location. A builder API for render conntexts along with making the
Highlighter
, and Layout
traits public would allow a user to inject a custom
layout.
Printing of Things
The renderable trait that is passed to a Layout
should expose the toc, title,
and content of the document. We can expose these as opaque items which implement
the Display
trait. That way we can format directly into whatever target buffer
is being written to.
E.g.:
trait Renderable { fn toc(&'a self, ctx: &'b RenderCtx) -> Toc<'a, 'b>; fn content(&'a self, ctx: &'b RenderCtx) -> Content<'a, 'b>; } type Content<'a> { toc: &'a TocTree; ctx: &'b RenderCtx; } impl<'a> Display for Content<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Create a highlighter iterator using the current highlighter, consuming // events from our main TOC. Pull those events down into Pulldown and have // it render directly to the output stream. html::write_html(Highlighted(self.ctx.highlighter, self.toc.iter())) .map_err(|_| fmt::Error) } }