Block Engines and Custom Definitions

- “Impact minus twenty seconds, guys. . . ” said the computer.

- “Then turn the bloody engines back on!” bawled Zaphod.

- “OK, sure thing, guys,” said the computer.

Douglas Adams, The Hitchhiker's Guide to the Galaxy (1979)

Block engines

Mau blocks have an advanced feature called block engine that rules the way the system processes the primary and secondary content.

Mau currently defines the following engines:

  • default
  • footnotes
  • group
  • mau
  • raw
  • source

When a block doesn't define a specific engine the default one is used.

The engines footnotes, group, and source will be shown in detail in the next chapters.

Default

As we saw in the previous chapter, the engine default processes the content of the block as Mau code using the variables defined previously in the document.

The text of the block is part of the Mau document, so variables, headers, and footnotes defined in the block become part of the document just as if they were defined outside the block.

This engine is used when no other engine is defined explicitly and is useful every time we need to customise the way the content is rendered in the final format but we want to keep the content as part of the document.

Quotes and admonitions use the default engine.

Mau

As we saw previously, the engine default processes the content as Mau code and adds variables, headers and footnotes in the current document. The engine mau instead treats primary and secondary content as isolated. You can use this engine in particular to keep headers from being collected and shown into the ToC.

For example, using a command ::toc: in a block rendered by the engine mau will include only the headers defined in the block itself.

Mau source
[engine=mau]
----
= Main section

== Secondary section

---
::toc:
----

Main section

Secondary section


Please note that with this engine the content of the block is still rendered in the current document. This means that with output formats like TeX that create their own TOC automatically such headers will still be part of the main document.

Raw

The engine raw is useful every time we want to include text in the output format directly, as this engine doesn't process the content at all. For example, you might want to add custom HTML code.

The following is an example from the UI kit I used for this website

Mau source
[engine=raw]
----
<button type="button" class="btn btn-primary">
  Notifications <span class="badge badge-light ml-1">4</span>
</button>
----

Please note that while the content of the block is not processed by Mau it is still rendered as any other block through templates. See Basic templates to find out how to include custom content without any wrapper.

Custom block definitions

As we saw previously, blocks have many attributes that you can set on them. In Basic templates and Advanced templates we will also see how you can create and use custom attributes.

Setting the same parameters over and over can become tedious and error prone, so Mau provides a way to define blocks through the command ::defblock:ALIAS, ARGUMENTS.

Mau source
::defblock:python, engine=source, language=python

[*python]
----
class MyException(Exception):
    pass
----

This is equivalent to

Mau source
[engine=source, language=python]
----
class MyException(Exception):
    pass
----

Mandatory arguments and defaults

The full syntax of a block definition is

Mau source
::defblock:ALIAS, [*SUBTYPE], [UNNAMED ARGUMENTS], [NAMED ARGUMENTS]

but the aliasing mechanism doesn't work as a pure substitution.

  • SUBTYPE is going to be the block subtype. If missing, the subtype will not be set on the block.
  • UNNAMED ARGUMENTS are interpreted as mandatory arguments.
  • NAMED ARGUMENTS are interpreted as defaults values.

For example, ::defblock:alias, *type1, name means that whenever we use *alias as the block subtype we also have to provide either an unnamed argument (which will be assigned the key name) or a named argument called name.

Mau source
::defblock:alias, arg1, key1=value1

[*alias] <--------------------------- This is invalid, as it is 
----                                  missing the unnamed argument
----

[*alias, alert] <-------------------- This is valid.
----                                  Unnamed arguments: []
----                                  Named arguments: {"arg1": "alert", "key1": "value1"}

[*alias, alert, red]  <-------------- This is valid.
----                                  Unnamed arguments: ["red"]
----                                  Named arguments: {"arg1": "alert", "key1": "value1"}

[*alias, alert, red, key1=value2] <-- This is valid.
----                                  Unnamed arguments: ["red"]
----                                  Named arguments: {"arg1": "alert", "key1": "value2"}

[*alias, red, arg1=alert] <---------- This is valid.
----                                  Unnamed arguments: ["red"]
----                                  Named arguments: {"arg1": "alert", "key1": "value1"}.

As you can see, block definitions do not work exactly like functions in programming languages. Block attributes can always contain any number of unnamed and named arguments, and the block definition just specifies which named ones we are always expecting to see.

Recursive subtypes

Block definitions are not recursive, so it is perfectly fine to write a definition like

Mau source
::defblock:alias, *alias, engine=raw, classes="myclass1,myclass2"

In this case the syntax [*alias] will be converted to [*alias, engine=raw, classes="myclass1,myclass2] and the block will still have the subtype alias.

Predefined blocks

Mau provides some aliases out of the box, namely source, footnote, and admonition. While these are created directly in the Python soruce code, they are equivalent to the following definitions

Mau source
::defblock:source, language=text, engine=source
TODO
::defblock:admonition, *admonition, class, icon, label

The built-in block type quote is not an alias and doesn't contain additional attributes, so it works just like a default block.

Block definitions in configuration

You can create block definitions through Mau's configuration setting the variable mau.parser.block_definitions

Mau source
"mau" : {
    "parser": {
        "block_definitions": {
            "alias": {
                "subtype": "type",
                "mandatory_args": ["arg1", "arg2"],
                "defaults": {"key1": "value1"},
            },
        },
    }
}