Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,44 @@ if (MRDOCS_BUILD_TESTS)
)
endforeach ()

#-------------------------------------------------
# Template-only generators
#
# Fixtures under test-files/template-only-generators/<id> ship an
# addon defining their own Handlebars generator. They live outside
# test-files/golden-tests so the xml/adoc/html runs do not walk
# into them and demand expected files in their own formats.
#-------------------------------------------------
set(MRDOCS_TEMPLATE_ONLY_ROOT "${PROJECT_SOURCE_DIR}/test-files/template-only-generators")
add_test(NAME mrdocs-golden-tests-mock-md
COMMAND
mrdocs-test
--unit=false
--action=test
"${MRDOCS_TEMPLATE_ONLY_ROOT}/mock-md"
"--addons=${CMAKE_SOURCE_DIR}/share/mrdocs/addons"
--generator=mock-md
"--stdlib-includes=${LIBCXX_DIR}"
"--libc-includes=${CMAKE_SOURCE_DIR}/share/mrdocs/headers/libc-stubs"
--log-level=warn
)
foreach (action IN ITEMS test create update)
add_custom_target(
mrdocs-${action}-test-fixtures-mock-md
COMMAND
mrdocs-test
--unit=false
--action=${action}
"${MRDOCS_TEMPLATE_ONLY_ROOT}/mock-md"
"--addons=${CMAKE_SOURCE_DIR}/share/mrdocs/addons"
--generator=mock-md
"--stdlib-includes=${LIBCXX_DIR}"
"--libc-includes=${CMAKE_SOURCE_DIR}/share/mrdocs/headers/libc-stubs"
--log-level=warn
DEPENDS mrdocs-test
)
endforeach ()

#-------------------------------------------------
# Self-documentation test (warn-as-error toggled by strict flag)
#-------------------------------------------------
Expand Down
44 changes: 44 additions & 0 deletions docs/modules/ROOT/pages/generators.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,50 @@ Mr.Docs strips Handlebars' trailing options object before forwarding arguments t
Helpers receive the positional arguments only and don't have to filter the options out themselves.
This also avoids expensive marshalling of symbol contexts, which contain circular references.

=== Custom output formats

Beyond the three built-in formats (`adoc`, `html`, `xml`), you can add an output format of your own by dropping a template directory under an addon root.
No C++ subclass is needed: Mr.Docs walks the immediate subdirectories of <addons>/generator/ at startup, and any subdirectory that ships an `mrdocs-generator.yml` file is installed as a new generator with id `<name>`.

The directory name is both the format id and the output file extension: the Handlebars Builder keys all template lookups on the file extension, so the two must agree.
To add a format that emits `.md` files, name the directory `md` and select it with `--generator=md`.

To add the format:

. Create <addons>/generator/md/mrdocs-generator.yml. The file declares the directory as a generator and supplies any escape rules; an empty file is fine when there are no rules to specify.
. Create <addons>/generator/md/layouts/index.md.hbs and <addons>/generator/md/layouts/wrapper.md.hbs. These have the same role they have for `adoc` and `html`: `index` renders one symbol; `wrapper` is interpolated around the index output and is responsible for the page title and any boilerplate.
. Optionally add <addons>/generator/md/partials/ and <addons>/generator/md/helpers/ for the partials and helpers your layouts use. The lookup paths and override rules are the same as for the built-in formats.
. Select the format on the command line or in your config with `generator: md`. Mr.Docs writes output files with the `.md` extension.

A directory under `generator/` that does not ship an `mrdocs-generator.yml` is treated as a shared-assets directory and skipped. That's how the built-in `common/` directory (which ships only CSS and shared partials) coexists with real generator directories without being mistakenly registered.

==== The `mrdocs-generator.yml` manifest

If your format needs per-pattern escape rules, drop a `mrdocs-generator.yml` file alongside `layouts/`:

[source,yaml]
----
escape:
'*': '\*'
'**': '<strong>'
'_': '\_'
'`': '\`'
----

The `escape` key is optional and holds a sub-mapping from byte-sequence keys to replacement strings.
A position in the input that matches no rule passes through unchanged.
Keys may be one or more bytes long; an empty key is rejected at startup.

Multi-byte keys are useful for tokens like Markdown's `**` (bold) versus a literal `*`, RST's ``` `` ``` (literal) versus ``` ` ``` (emphasis), or for whole UTF-8 codepoints that should be replaced as a unit.
When more than one rule could match at a position, the longest match wins, so a `**` rule takes precedence over a `*` rule when both are present.

Unknown top-level keys are silently ignored so future schema additions stay non-breaking.

==== Layering across addon roots

If the same id appears under more than one addon root, the first one wins: that root's manifest sets the format's escape rules.
Later roots can still contribute layered partials and helpers under the same id through the existing template-loading path, so a project can supplement a shared format without redefining it.

== Stylesheet Options

The HTML and AsciiDoc generators ship a bundled stylesheet that is inlined by default. You can replace or layer styles with the following options (available in config files and on the CLI):
Expand Down
10 changes: 3 additions & 7 deletions docs/mrdocs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,13 +252,9 @@
},
"generator": {
"default": "adoc",
"description": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The generator can create different types of documentation such as HTML, XML, and AsciiDoc.",
"enum": [
"adoc",
"html",
"xml"
],
"title": "Generator used to create the documentation"
"description": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The built-in generators include `adoc`, `html`, and `xml`; addon-defined generators can be added by dropping a template folder under <addon>/generator/<name>/.",
"title": "Generator used to create the documentation",
"type": "string"
},
"global-namespace-index": {
"default": true,
Expand Down
13 changes: 13 additions & 0 deletions include/mrdocs/Support/Handlebars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,19 @@ HTMLEscape(
OutputRef& out,
std::string_view str);

/** Character-to-entity table used by `HTMLEscape`.
*/
inline constexpr std::pair<char, std::string_view>
htmlEscapeEntities[] = {
{'&', "&amp;"},
{'<', "&lt;"},
{'>', "&gt;"},
{'"', "&quot;"},
{'\'', "&#x27;"},
{'`', "&#x60;"},
{'=', "&#x3D;"}
};

/** \brief HTML escapes the specified string.
*
* This function HTML escapes the specified string, making it safe for
Expand Down
9 changes: 2 additions & 7 deletions src/lib/ConfigOptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -397,13 +397,8 @@
{
"name": "generator",
"brief": "Generator used to create the documentation",
"details": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The generator can create different types of documentation such as HTML, XML, and AsciiDoc.",
"type": "enum",
"values": [
"adoc",
"html",
"xml"
],
"details": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The built-in generators include `adoc`, `html`, and `xml`; addon-defined generators can be added by dropping a template folder under <addon>/generator/<name>/.",
"type": "string",
"default": "adoc"
},
{
Expand Down
8 changes: 7 additions & 1 deletion src/lib/Gen/adoc/AdocGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@

#include "AdocGenerator.hpp"
#include "AdocEscape.hpp"
#include <mrdocs/Support/Handlebars.hpp>
#include <memory>

namespace mrdocs {
namespace adoc {

AdocGenerator::
AdocGenerator()
: HandlebarsGenerator("adoc", "adoc", "Asciidoc")
{
}

void
AdocGenerator::
escape(OutputRef& os, std::string_view const str) const
Expand Down
21 changes: 1 addition & 20 deletions src/lib/Gen/adoc/AdocGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,15 @@
#ifndef MRDOCS_LIB_GEN_ADOC_ADOCGENERATOR_HPP
#define MRDOCS_LIB_GEN_ADOC_ADOCGENERATOR_HPP

#include <mrdocs/Platform.hpp>
#include <lib/Gen/hbs/HandlebarsGenerator.hpp>
#include <mrdocs/Generator.hpp>

namespace mrdocs::adoc {

class AdocGenerator final
: public hbs::HandlebarsGenerator
{
public:
std::string_view
id() const noexcept override
{
return "adoc";
}

std::string_view
fileExtension() const noexcept override
{
return "adoc";
}


std::string_view
displayName() const noexcept override
{
return "Asciidoc";
}
AdocGenerator();

void
escape(OutputRef& os, std::string_view str) const override;
Expand Down
Loading
Loading