Documentation
This is the complete documentation of emerald. It explains all of emerald's features and also explains implementation details, so it is also the developer documentation. The reader is expected to be used to the Nim programming language and its concepts.
emerald tries to omit HTML that is valid polyglot markup, i.e. that is valid HTML 5 and also valid XHTML. This means that emerald writes properly closed HTML tags in all cases, uses quotes for attribute values, and writes a value for boolean attributes where HTML permits the value to be omitted. The goal is for emerald's output to be as robust as possible.
Like Nim itself, emerald treats all keywords, commands and
procs it declares independently of style and casing, so you can
use both mixin_content
and mixinContent
as you please. HTML tag and
attribute names are also parsed case-independently; however,
the styling matters here: http_equiv
is not the same as httpEquiv
.
Interface
The two macros html_templ
and html_mixin
are the main API of emerald. The only
other things that are publicly exposed are the filters that
come with emerald. These are explained in Filters.
html_templ
This macro can only be applied to a proc. This proc may not
have a return type and may be publicly exposed (via *
) or private. The whole content of the proc will
be parsed as HTML template. Parsing will convert the proc into
an object type with the same name, a constructor proc for this
type, and a method named render()
that operates on this type. This is shown in the example code.
The render
method takes an instance of the
template object as first parameter and a Stream
as second parameter. The object type, the constructor proc
and the render method will have the same visibility as the
original proc, so you can have private and public templates.
All parameters of the original proc will be transformed into fields of the resulting object type. This enables you to re-use an object instance multiple times without needing to specify all parameters each time.
html_mixin
This macro must also be applied to a proc that has no return
value. Parsing it will not produce any nodes in the resulting
AST; instead, it is parsed directly into the render
proc at each place where it is called. There
are two reasons for doing that: Firstly, it enables emerald to
validate the HTML structure at each position where it is called.
Secondly, the resulting HTML is properly indented everywhere.
Mixins can be called with the call_mixin
command. The command takes the call to the mixin as first
parameter; you have to give all parameters you defined for the
mixin there. You can also give a block as second parameter, in
which case this block can be called from within the mixin by
calling mixin_content()
. If you call mixin_content()
in the mixin code, but do not
supply a block as parameter in the template where you call the
mixin, emerald will exit with an error message.
Tags
In an HTML template, every standalone call is interpreted as HTML tag. Standalone means that the call is not part of an expression, or in simpler terms: The call is written on its own line with nothing else there. You can of course condense multiple lines by using semicolons to make your code more compact. Standalone calls may, but do not need to, have a child block. The structure of the tags is validated according to the HTML 5 specification. Infix expressions are not considered to be calls; they will be parsed as text content generators (see below).
The content of a tag may also be given as direct content, meaning that it is written as
parameter into the brackets of the tag, rather than into the
child block. This may be convenient for tags that usually occur
between character data, like e.g. <strong>
.
If a tag name is also a keyword name in Nim, you have to put
the tag name between accents, like this: `object`
. As a special feature, you can write d
for <div>
tags, because they are pretty common and div
is a keyword in Nim.
You can give tags classes by using the dot notation, as
illustrated in the example. Tags can have any number of classes.
The dot notation is somewhat limited, because you cannot use
expressions for defining the class name, so you can also set the
attribute class
. You can use both dot notation
and the class
attribute on one tag.
The html
tag is kind of a special feature. It
automatically emits the HTML 5 doctype, gets a proper XML
namespace definition, and its required attribute lang
gets automatically copied into the attribute xml:lang
, which is necessary for valid XHTML. That
does not mean that you must start every template with an html
tag - it is perfectly fine to write templates
which only generate a part of an HTML DOM-tree. You would
use such templates e.g. for AJAX-based websites.
Attributes
For each tag, attributes can be specified in the brackets of
the tag call. The name of the attribute comes first, followed by
an equals sign and the value of the attribute. The value may be
any Nim expression. Attributes which are specified to be
boolean, such as checked
or readonly
, must have an expression of Nim type bool
as value. All other attribute values will be converted into
strings.
Like tags, attributes are validated by emerald. A missing required attribute and an attribute that is not allowed for the tag that contains it both lead to an error message.
Attribute values are automatically filtered. Unlike the
filtering of normal text, the filtering of attribute values
cannot be customized, because there is no use-case for that
(please file an issue if you can think of one). Filtering will
convert the characters <
, >
, &
, "
and '
to their corresponding HTML entities.
Some HTML attributes contain a -
in their name.
This cannot be a part of a Nim identifier. Therefore, you must
use a _
instead. So, for example, you have to
write http_equiv
instead of http-equiv
. Also be aware that attribute names are
case and style sensitive.
Data Attributes
HTML 5 allows any HTML tag to have an arbitrary number of data attributes, named like this: data-*
emerald treats these values as a table, meaning that you can
assign the data
attribute a table constructor. This constructor must have string literals as keys, so that
emerald can check the validity of the names at compile time - it
doesn't make much sense to define the data attribute names with
variables anyway. The value of each data attribute may be any
expression. The example shows how to set data attributes.
Text Content
Text content is generated by every expression that is used as
a statement, excluding calls (which are used for creating HTML
tags). The simplest way to generate text content is to use
string literals. Infix expressions also generate text content,
although they are technically calls. The operator $
is used to transform any value into a string for outputting
it.
If you want to call a proc and output the result as text
content, you have to use the command put
,
because normal calls are interpreted as HTML tags. If you just
want to call a proc without using its return value - or a proc
which does not have a return value at all - use discard
.
All text content is processed by the current filter chain
before being written to the output. By default, this converts
the HTML characters <
, >
and &
to their corresponding HTML entities. The filter
chain can be customized to your needs, see the next section.
Control structures
Most of Nim's control structures are directly usable in
emerald: You can use if
, case
and while
just like you would in Nim code.
You can also declare and assign variables in your template.
However, you cannot do everything in emerald you could do in
Nim, and if you need to write logic that spans more than a few
lines, it is probably a good idea to write it in a proper Nim
proc and call that from within your template.
Pragmas
You can modify the way emerald compiles your template by
using pragmas. Pragmas use the usual Nim syntax {. pragma here .}
. emerald supports the following pragmas:
-
{. compact_mode = val .}
- Toggles whether the generated HTML should be written
in human-readable form with newlines and indentation, or as
compact as possible without any unnecessary whitespace. val may be either
true
orfalse
, default value isfalse
. -
{. indent_step = val .}
- Sets the amount of spaces added to every new level of
indentation. val may be any non-negative
integer value. default is
4
. -
{. preserve_whitespace = val .}
- Defines whether the lines of generated text content will
be indented to the current output indentation, removing any
existing indentation. val may be
true
orfalse
, default isfalse
. Iftrue
, the existing whitespace at the beginning of each line for text output will be preserved and no indentation will be applied ( regardless of the value ofcompact_mode
. This is useful when inserting source code or anything similar in your HTML page. -
{. debug = val .}
- Enables or disables debugging output. If enabled,
emerald will output the generated AST as Nim code to
stdout. val may be
true
orfalse
, default isfalse
. -
{. filters = filter_chain .}
- This pragma manipulates the filter chain and is described in detail in the next section.
Apart from debug
, all pragmas are applied to
the current level of your template code, not globally. That
means that the new pragma value you have set only affects the
content of the current HTML tag and all tags within, but not
the part of the template outside the current HTML tag.
Filters
emerald maintains a filter chain while compiling your
template. All text content is processed by the current filter
chain. The filter chain may contain zero or more filters. A
filter is a proc that takes a string as input and writes data
to an output stream. By default, the filter chain contains one
filter, escape_html
, which converts HTML
special characters to their corresponding entities.
You can modify the filter chain by using the pragma filters
, which allows you to specify the filter
chain as a list of filters separated by &
operators. You may use the identifier filters
when setting this chain to insert the previously active
filters. You may use nil
to specify that no
filter at all should be used. The code example shows how to use
the pragma.
Filters may take additional parameters. You can set them in brackets as seen in the example.
Builtin Filters
-
escape_html(escapeQuotes: bool = false)
- Convert HTML special chars to their corresponding
entities. if
escapeQuotes
istrue
,"
and'
are escaped as well. -
rst(options: TRstParseOptions = {}, config: StringTableRef = newStringTable())
- Parses the input as RST, using Nim's internal RST implementation. Be aware that the resulting HTML is not validated by emerald.
-
pygmentize(language: string)
- Uses pygments to add syntax highlighting to text output. Pygments must be available on your system in order to use this filter. You have to include a pygments css theme in order to actually see the syntax highlighting in the rendered output.
Writing Your Own Filters
Filters are simple procs. If you want to write your own filter, you just implement it as proc. It should look like this:
proc myFilter(target: Stream, value: string, ...)
At ...
, you can add your own parameters. You
need to give values for any non-optional parameters you add here
when using the filter. The first two parameters are added by
emerald when you use the filter in the filter chain.
When the filter gets called, you should process value
and write the result to target
. That's all.
Template Inheritance
You can inherit from templates by specifying the parent template when declaring the child template. This will make the generated object type of the child template inherit from the object type of the parent template.
In any template that inherits from another template, you
cannot have HTML tags or text content nodes on the root level.
However, you can assign values to the parent template's
parameters. For adding content, you use the following commands:prepend
, replace
and append
. Each of these takes one argument and must
have a child block. The argument must be the name of a block in
any parent template (does not need to be the immediate parent). prepend
will add its content before the
content of the block in the parent template, replace
will completely replace the content of the
block, and append
will append its content to
the block in the parent template.
Blocks must always have names in templates. You may still use them for scoping variables as you can do it in Nim, but be aware that each block will be compiled into a multimethod.