Handlebars Extensions
Mr.Docs renders the documentation for the html and adoc generators from a tree of Handlebars templates that ship under <mrdocs-root>/share/mrdocs/addons/generator/<generator-name>.
addons/
generator/
html/ HTML-specific templates and helpers.
adoc/ AsciiDoc-specific templates and helpers.
common/ Shared partials and helpers
Built-in Layout
Each handlebars generator walks the symbol corpus and recursively renders layouts/index.adoc.hbs for each symbol. The layouts call {{> … }} to include partials that render the symbol’s name, signature, doc comment, and so on.
addons/
generator/
html/
layouts/
index.adoc.hbs
partials/
...
...
These templates call helpers whenever they need more than a plain field interpolation: looking up a symbol’s primary source location, lowercasing a string, joining a list, escaping a URL.
addons/
generator/
html/
helpers/
my-function.js
my-other-function.lua
...
...
...
Customizing the output is a matter of dropping replacement .hbs files or .js helpers into an addon directory. Under each generator directory, you’ll find three subtrees that matter for customization:
layouts/-
The main top-level templates the generator picks. The
<generator>/layouts/index.adoc.hbsis rendered for each symbol. In multipage mode, each page is then wrapped with<generator>/layouts/wrapper.adoc.hbs. In single-page mode, the wrapper is used only once to wrap a single page around the whole corpus. Setembeddedto skip the wrapper entirely and emit only the per-symbol body, for when the output is being included inside another document. partials/-
The fragments those layouts include with
{{> … }}. Files are grouped by topic (symbol/,doc/,type/,markup/,location/). A symbol page is essentially a layout that calls{{> symbol/qualified-name-title }}, then{{> symbol/signatures }}, then renders the doc comment via{{> doc/block/document }}, and so on. helpers/-
JavaScript or Lua files registered as Handlebars helpers. Anything in
common/helpers/is registered for every template-driven generator; anything under<format>/helpers/is registered only when that format is the active one.
The common/ directory contains partials that are shared across generators, such as symbol signatures. The format-specific directories (adoc/, html/) ship only the partials for the markup that distinguishes the output between formats, such as link emission, code fencing, or table syntax. This makes it extremely easy to implement support for new output formats.
Customization
Two configuration options control how Mr.Docs locates templates and helpers:
addons-
Replaces the base addons directory entirely. Pointing this at a copy of
share/mrdocs/addonslets you rewrite the templates wholesale. addons-supplemental-
Layers extra directories on top of the base. Files in later supplemental directories override files from earlier ones and from the base addons. Use this to override a few templates or helpers without copying the entire tree.
For most customizations addons-supplemental is the option you want: you create a small directory that mirrors the upstream layout for just the files you want to change, and Mr.Docs falls back to its built-in templates for everything else.
Overriding a template
Suppose every code block in the rendered output should carry a small bit of chrome around it: a copy-to-clipboard button in the HTML output, a Listing caption in the AsciiDoc output. The base partial that wraps each code block lives at generator/<format>/partials/markup/code-block.<format>.hbs. Override only that one file, rather than rewriting any layout.
-
Create a directory next to your
mrdocs.ymlthat mirrors the addons layout. Each override file lives at the same relative path under the supplemental directory as the upstream file does undershare/mrdocs/addons/:docs/ mrdocs.yml custom-addons/ generator/ html/ partials/ markup/ code-block.html.hbs adoc/ partials/ markup/ code-block.adoc.hbs -
Put the customization in each
code-block.<format>.hbs. The HTML version wraps the inner block in a copy-button container; the AsciiDoc version prepends a.Listingblock title to each listing:-
HTML
-
AsciiDoc
custom-addons/generator/html/partials/markup/code-block.html.hbs{{!-- Override of share/mrdocs/addons/generator/html/partials/markup/code-block.html.hbs --}} <div class="code-block-wrapper"> <button class="copy-button" type="button" aria-label="Copy code">Copy</button> <pre><code class="source-code cpp">{{> @partial-block }}</code></pre> </div>custom-addons/generator/adoc/partials/markup/code-block.adoc.hbs{{!-- Override of share/mrdocs/addons/generator/adoc/partials/markup/code-block.adoc.hbs --}} .Listing [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- {{> @partial-block }} ---- -
-
Point
mrdocs.ymlat the directory:addons-supplemental: - custom-addons
Run that against a small fixture:
override-code-block.cpp/// Square `value` and return the result.
double square(double value);
The base addons still provide every other partial; only markup/code-block.<format>.hbs is replaced. The rendered AsciiDoc page now shows the Listing caption above each synopsis block; the HTML page would carry the copy-button wrapper through the same mechanism:
square
Square value and return the result.
Synopsis
Declared in <override‐code‐block.cpp>
double
square(double value);
Reordering the Main Template
Mr.Docs provides a partial template for each symbol page that renders the synopsis, doc comment, and examples in a standard order. A common pattern used by projects is to document the description and examples first and then present the synopsis at the end of each page. Besides customizing the ordering of the sections in this main template, a few placeholders such as @seebelow need to be adapted:
-
Config
-
Section order
-
Placeholder
-
Input
override-see-below.ymladdons-supplemental:
- addons
addons/generator/common/partials/symbol.hbs{{!--
Project override: render the synopsis last, after the description, so the
prose and examples come first on the page. Only the position of
`symbol/section/synopsis` changes from the base template.
--}}
{{#> markup/section name="symbol"}}
{{> symbol/section/header}}
{{> symbol/section/attribute-admonitions ~}}
{{> symbol/section/function-object-admonition}}
{{> symbol/section/description}}
{{> symbol/section/base-classes}}
{{> symbol/section/protected-base-classes}}
{{> symbol/section/members}}
{{> symbol/section/specializations}}
{{> symbol/section/deduction-guides}}
{{> symbol/section/friends}}
{{> symbol/section/using-directives}}
{{> symbol/section/non-member-functions}}
{{> symbol/section/derived-classes}}
{{> symbol/section/introduced-symbols}}
{{> symbol/section/noexcept-specification}}
{{> symbol/section/exceptions}}
{{> symbol/section/return-value}}
{{> symbol/section/template-parameters}}
{{> symbol/section/parameters}}
{{> symbol/section/preconditions}}
{{> symbol/section/postconditions}}
{{> symbol/section/see-also}}
{{> symbol/section/synopsis}}
{{> symbol/section/recursive-index}}
{{> symbol/section/footnotes}}
{{/markup/section}}
addons/generator/common/partials/symbol/signature/see-below.hbs{{ str '/*' }} see above {{ str '*/' }}
override-see-below.cppnamespace net {
/** A move-only handle to an open TCP connection.
Obtain one from `connect`; it closes the socket when it goes out of scope.
Send and receive through the free functions in this namespace:
@code
auto c = net::connect("example.org", 80);
net::write(c, request);
@endcode
Its representation is an implementation detail that changes between releases,
so the definition is not shown here; use it only through the operations
described above.
@seebelow
*/
class connection
{
int fd_;
public:
/// Close the connection.
void close();
};
}
The page then reads discussion-first and the elided synopsis points back up:
net::connection
A move‐only handle to an open TCP connection.
Description
Obtain one from connect; it closes the socket when it goes out of scope. Send and receive through the free functions in this namespace:
auto c = net::connect("example.org", 80);
net::write(c, request);
Its representation is an implementation detail that changes between releases, so the definition is not shown here; use it only through the operations described above.
Synopsis
Declared in <override‐see‐below.cpp>
class connection { /* see above */ };
Customizing helpers
Built-in helpers
Mr.Docs registers the default helpers before any user code runs. They cover:
-
String utilities:
lower,upper,capitalize,replace,pad_start,repeat,strip, and the rest of a Python-style string library. -
Logical and math helpers:
eq,ne,and,or,not,add,sub,mul,div. -
Container helpers:
len,first,last,at,keys,values,filter_by,group_by,pluck,flatten. -
Antora-aware link helpers, most notably
relativizefor rewriting an absolute target into a path relative to the current page. -
Mr.Docs-specific helpers like
primary_location, which picks the source location Mr.Docs links to from a symbol’s synopsis.
The DOM reference documents the values that helpers receive (symbol fields, document blocks, type nodes) so a helper knows what shape it is reading.
Adding a helper
A user helper is a JavaScript or Lua file that lives under an addons helpers/ directory and is picked up automatically. The filename without extension becomes the helper name: slug.js registers a helper called slug, markdown.lua registers one called markdown, and so on. The file’s top-level function (or its module.exports in JS) is the helper body.
A small URL-slug helper, reusable across formats, lives under common/helpers/:
addons/generator/common/helpers/slug.js// A URL-safe slug: lowercased, runs of non-alphanumeric characters collapsed
// to a single `-`, and leading and trailing dashes removed.
function slug(name)
{
if (!name) {
return '';
}
return String(name)
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
}
Calling the helper from a template needs a partial override. The base location/source.hbs emits the "Declared in <file>`" line at the top of every symbol page; the override appends a permalink-style anchor next to it, derived from the file path via `{{slug …}}:
addons/generator/common/partials/location/source.hbs{{!--
Override of share/mrdocs/addons/generator/common/partials/location/source.hbs.
Appends a permalink-style anchor after "Declared in", built from
the file path via the `slug` helper.
--}}
Declared in {{#>markup/code~}}
{{ str "<" }}{{ dcl.shortPath }}{{ str ">" }}
{{~/markup/code}}{{ str " " }}(anchor: {{#>markup/code}}{{slug dcl.shortPath}}{{/markup/code}})
Run that against a single-function fixture:
helper-slug.cpp/// Compute the Euclidean distance between two points.
double distance(double x1, double y1, double x2, double y2);
Wire the addon directory up with the same addons-supplemental option used for templates:
addons-supplemental:
- addons
Each symbol page now carries the anchor right next to its "Declared in" line, slugged from the file path the override passed to {{slug …}}:
distance
Compute the Euclidean distance between two points.
Synopsis
Declared in <helper‐slug.cpp> (anchor: helper‐slug‐cpp)
double
distance(
double x1,
double y1,
double x2,
double y2);