Data-Driven Generators
Beyond the three built-in generators, you can add an output format of your own by dropping a template directory under either an addon root or an supplemental addon root. Mr.Docs walks the immediate template subdirectories of every Handlebars generator it finds at startup, and any subdirectory that ships an mrdocs-generator.yml file is installed as a new generator.
The manifest
The manifest’s presence is what marks a directory as a data-driven handlebars generator: any directory with <addons>/generator/<name>/mrdocs-generator.yml declares the directory as a data-driven generator. Alongside the manifest, each generator owns two layouts under <addons>/generator/<name>/layouts/:
-
index.<name>.hbsrenders a single symbol, and -
wrapper.<name>.hbsis interpolated around the index output to provide the page title and any boilerplate.
The partials/ and helpers/ subdirectories are technically optional and follow the same lookup rules as the built-in formats (see Handlebars Extensions).
For instance, consider the jsonl generator that ships under examples/generators/data-driven/jsonl/. Each symbol becomes one JSON object on its own line (the JSON Lines convention) so a client search box can ingest the file streamably. A similar pattern can be used to define a generator for navigation sidebars. The manifest only sets escape rules for the two characters that must be quoted inside a JSON string:
addons/generator/jsonl/mrdocs-generator.ymlescape:
'"': '\"'
'\': '\\'
The escape: key holds a sub-mapping from byte-sequence keys to replacement strings.
We can now define the handlebars templates. The wrapper here is a pass-through, so the index template always owns every line of output:
addons/generator/jsonl/layouts/wrapper.jsonl.hbs{{{contents}}}
addons/generator/jsonl/layouts/index.jsonl.hbs{{#unless (eq symbol.kind "namespace")~}}
{"name": "{{>symbol/qualified-name-text symbol}}", "kind": "{{symbol.kind}}", "anchor": "{{symbol.anchor}}", "brief": "{{#if symbol.doc.brief.children}}{{#each symbol.doc.brief.children}}{{literal}}{{/each}}{{/if}}"}
{{/unless}}
The fixture’s input:
simple.cpp/// A small two-dimensional point.
struct Point
{
/// Distance from the origin.
double length() const;
/// Translate by an offset.
void translate(int dx, int dy);
};
Once everything is in place, the format is selectable through the generator option, just like any built-in format. Mr.Docs writes output files with the .<name> extension.
generator: jsonl
Or on the command line, where the flag wins over the file:
mrdocs --config=path/to/mrdocs.yml --generator=jsonl
Rendered output:
simple.jsonl{"name": "Point", "kind": "record", "anchor": "Point", "brief": "A small two-dimensional point."}
{"name": "Point::length", "kind": "function", "anchor": "Point-length", "brief": "Distance from the origin."}
{"name": "Point::translate", "kind": "function", "anchor": "Point-translate", "brief": "Translate by an offset."}
Inheriting templates
A second key on the manifest, extends: <id>, makes the generator’s partial and helper lookup fall back to another installed generator after consulting its own directory.
|
Lookup precedence
For any partial or helper, the lookup walks these directories from lowest to highest precedence:
An own file overrides the parent’s, and the parent’s overrides |
The two examples below stack on each other:
-
The Markdown generator extends the built-in HTML one, because any valid HTML is already valid Markdown content. Most HTML partials are reused in Markdown, which then swaps in its flavored headings, bold, code spans, and code blocks.
-
LaTeX then extends Markdown because most of what the Markdown generator produces is also acceptable inside a LaTeX document for the template we’re using. The only things that need to change are the wrapping and a few markup primitives.
Each manifest declares its parent and sets its own escape rules:
-
Markdown
-
LaTeX
addons/generator/md/mrdocs-generator.ymlextends: html
escape:
'\': '\\'
'_': '\_'
'*': '\*'
'`': '\`'
'[': '\['
']': '\]'
addons/generator/tex/mrdocs-generator.ymlextends: md
escape:
'\': '\textbackslash{}'
'_': '\_'
'&': '\&'
'%': '\%'
'#': '\#'
'$': '\$'
Each generator owns its wrapper. The Markdown wrapper is a single heading. The LaTeX wrapper is a small article preamble.
-
Markdown
-
LaTeX
addons/generator/md/layouts/wrapper.md.hbs# Reference
{{{contents}}}
---
*Created with [MrDocs](https://www.mrdocs.com)*
addons/generator/md/layouts/index.md.hbs{{> symbol }}
addons/generator/tex/layouts/wrapper.tex.hbs\documentclass{article}
\title{Reference}
\begin{document}
\maketitle
{{{contents}}}
\vfill
\hrule
\vspace{0.5em}
\emph{Created with MrDocs (https://www.mrdocs.com)}
\end{document}
addons/generator/tex/layouts/index.tex.hbs{{> symbol }}
The actual format differences live in partials/markup/. Each leaf ships a handful of markup overrides. The rest of the HTML pipeline reaches for the inherited ones and embeds them directly.
-
Markdown
-
LaTeX
addons/generator/md/partials/markup/strong.md.hbs**{{> @partial-block }}**
addons/generator/md/partials/markup/em.md.hbs*{{> @partial-block }}*
addons/generator/md/partials/markup/code.md.hbs`{{> @partial-block }}`
addons/generator/md/partials/markup/h.md.hbs{{repeat "#" (or level 3)}} {{> @partial-block }}
addons/generator/md/partials/markup/code-block.md.hbs```cpp
{{> @partial-block }}{{!--keep-newline--}}
```
addons/generator/tex/partials/markup/strong.tex.hbs\textbf{ {{~> @partial-block ~}} }
addons/generator/tex/partials/markup/em.tex.hbs\emph{ {{~> @partial-block ~}} }
addons/generator/tex/partials/markup/code.tex.hbs\texttt{ {{~> @partial-block ~}} }
addons/generator/tex/partials/markup/h.tex.hbs{{!--
LaTeX heading.
The article class only defines `\section`, `\subsection`,
`\subsubsection`, `\paragraph`, `\subparagraph` -- no level above
`\section`. The common symbol templates use level=2 for the
symbol top title (matching the HTML convention where the wrapper
supplies h1 and the symbol title sits at h2), so we shift one
level down here: level<=2 maps to `\section`, level=3 to
`\subsection`, level=4 to `\subsubsection`, and anything deeper
to `\paragraph`.
--}}
{{#if (or (eq (or level 3) 1) (eq (or level 3) 2))}}\section{ {{~> @partial-block ~}} }
{{else if (eq (or level 3) 3)}}\subsection{ {{~> @partial-block ~}} }
{{else if (eq (or level 3) 4)}}\subsubsection{ {{~> @partial-block ~}} }
{{else}}\paragraph{ {{~> @partial-block ~}} }
{{/if}}
addons/generator/tex/partials/markup/code-block.tex.hbs\begin{verbatim}
{{> @partial-block }}{{!--keep-newline--}}
\end{verbatim}
The same shape of input as the jsonl section drives both formats:
simple.cpp/// A small two-dimensional point.
struct Point
{
/// Distance from the origin.
double length() const;
/// Translate by an offset.
void translate(int dx, int dy);
};
Rendered outputs. Both files mix inherited HTML structure (residual <div> blocks and tables) with the format-specific markup the overrides supplied. Anything not covered by an override stays HTML, which the surrounding format accepts:
-
Markdown
-
LaTeX
simple.md# Reference
## Point
A small two-dimensional point.
### Synopsis
Declared in `<simple.cpp>`
```cpp
struct Point;
```
### Member Functions
| Name | Description |
| --- | --- |
| [`length`](#Point-length) | Distance from the origin. |
| [`translate`](#Point-translate) | Translate by an offset. |
## [Point](#Point)::length
Distance from the origin.
### Synopsis
Declared in `<simple.cpp>`
```cpp
double
length() const;
```
## [Point](#Point)::translate
Translate by an offset.
### Synopsis
Declared in `<simple.cpp>`
```cpp
void
translate(
int dx,
int dy);
```
---
*Created with [MrDocs](https://www.mrdocs.com)*
simple.tex\documentclass{article}
\title{Reference}
\begin{document}
\maketitle
\section{Point}
A small two-dimensional point.
\subsection{Synopsis}
Declared in \texttt{<simple.cpp>}
\begin{verbatim}
struct Point;
\end{verbatim}
\subsection{Member Functions}
\begin{tabular}{ll}
\textbf{Name} & \textbf{Description} \\
\hline
\texttt{length} & Distance from the origin. \\
\texttt{translate} & Translate by an offset. \\
\end{tabular}
\section{Point::length}
Distance from the origin.
\subsection{Synopsis}
Declared in \texttt{<simple.cpp>}
\begin{verbatim}
double
length() const;
\end{verbatim}
\section{Point::translate}
Translate by an offset.
\subsection{Synopsis}
Declared in \texttt{<simple.cpp>}
\begin{verbatim}
void
translate(
int dx,
int dy);
\end{verbatim}
\vfill
\hrule
\vspace{0.5em}
\emph{Created with MrDocs (https://www.mrdocs.com)}
\end{document}