Awesome
lcmark
lcmark
is a high-level CommonMark converter
built upon cmark
. It comes as both a
command-line program and a Lua module. It supports:
-
YAML metadata at the top of the document.
-
Filters, which allow the document to be transformed between parsing and rendering, making a large number of customizations possible.
-
Templates, which allow the body and metadata values to be embedded into a pre-defined structure.
Also see the documentation.
Installation
To install: luarocks install lcmark
(This installs both the library and the program).
Additionally, you'll also need a YAML parsing library. lcmark
will
automatically attempt to load and use one of
yaml,
lua-yaml or
lyaml, so make sure you have one of those
installed. Alternatively, a custom parser can be used (see the yaml_parser
option below).
Features
YAML Metadata
The YAML metadata section (if present) must occur at the beginning of the
document. It begins with a line containing ---
and ends with a line
containing ...
or ---
. Between these, a YAML key/value map is expected.
String values found in the metadata will be parsed and rendered as CommonMark. If a string value contains only a single paragraph, it will be rendered as an inline string.
If the yaml_parser
option (a function) is provided, lcmark
will use it to
parse YAML. The function should take a string as input and should return a
table. In case of failure, it should either throw an error or return a
nil, err
tuple; other returns will be discarded silently.
Example:
---
# This is a comment!
# Note that the quotes below are needed because of the
# colon in the title:
title: 'This is my *article*: subtitle here'
author:
- name: Sam Smith
institute: U of X
- name: Sasha Xi
institute: NXQ
abstract: |
Here is a multiline abstract.
- It can even
- contain
- lists and other block elements
...
Document body starts here...
Filters
Filters modify the parsed document prior to rendering.
A filter is a function that takes three arguments (doc
, meta
, to
), where
doc
is a cmark node, meta
is the YAML metadata as a (potentially nested) Lua
table with all strings replaced with cmark nodes, and to
is a string
specifying the output format (the same string as passed to lcmark.convert
).
The filter may destructively modify doc
and meta
.
Some sample filters are provided in
filters/
.
Templates
Templates are used to build standalone documents from the parsed document body and metadata.
lcmark
supports a small subset of the templating language used by
pandoc, and lcmark
templates can be used with pandoc
(with the caveat that pandoc sets many variables automatically that lcmark
does not).
nil
, false
and {}
(an empty table) are considered to be "falsy" values.
Any other value is considered "truthy".
A quick guide:
-
The only special character in templates is
$
. To get a literal$
character, use$$
. -
$name$
will be replaced with the value of thename
metadata field. Variable names can contain alphanumerics,-
, and_
. -
$name.subname$
will be replaced with the value of thesubname
field of thename
metadata field (assumed to be a map). Multiple indexes can be chained together this way. -
$if(name)$...$endif$
will be replaced by the content in...
if the value of thename
metadata field is "truthy", otherwise by nothing....
may contain nested templating directives. -
$if(name)$...$else$,,,$endif$
will be replaced by the content in...
ifname
has a truthy value, and by the content in,,,
otherwise. Both...
and,,,
may contain nested templating directives. -
$for(name)$...$endfor$
is a loop, producing successive concatenated copies of...
. If the value ofname
is a non-empty table, then in each occurrence of...
, the value ofname
will be replaced by a different element from the table (in order). For example,$for(authors)$$authors$$endfor$
will concatenate all the values of theauthors
table.Otherwise, if the value of
name
isn't a table, the loop behaves like anif
. -
$for(name)$...$sep$,,,$endfor$
behaves like the above, except that the content in,,,
is inserted between each copy of...
.,,,
supports nested templating directives.
Additionally, if newlines occurs directly after both $for()$
and
$endfor$
(or $if()$
and $endif$
), they will be ignored. This is to
prevent spurious blank lines in the rendered document if the template contains
many directives that span multiple lines and evaluate to false.
Some sample templates are provided in
templates/
.
Documentation
Program
lcmark --help
will print a short list of options.
For a more detailed description, see the lcmark(1)
man page.
Module
Basic usage:
local lcmark = require("lcmark")
local body, metadata = lcmark.convert("Hello *world*",
"latex", {smart = true, columns = 40})
The module exports the following fields:
-
lcmark.version
: a string with the version number. -
lcmark.yaml_parser_name
: a string holding the name of the automatically-loaded module and function used to parse YAML. Possible values are:"lyaml.load"
(lyaml)"yaml.load"
(yaml)"yaml.eval"
(lua-yaml)nil
(none)
-
lcmark.convert(str, to, options)
: Convertsstr
(a CommonMark formatted string) to the output format specified byto
(a string; one ofhtml
,commonmark
,latex
,man
, orxml
).options
is a table with the following fields (all optional):smart
- enable "smart punctuation"hardbreaks
- treat newlines as hard breakssafe
- filter out potentially unsafe HTML and linkssourcepos
- include source position in HTML and XML outputfilters
- an array of filters to run (seeload_filter
below)columns
- column width, or 0 to preserve wrapping in inputyaml_metadata
- whether to parse initial YAML metadata blockyaml_parser
- a function to parse YAML with (see YAML Metadata)
Returns
body
,meta
on success, wherebody
is the rendered document body andmeta
is the YAML metadata as a table. If theyaml_metadata
option is false or if the document contains no YAML metadata,meta
will be an empty table. In case of an error, the function returnsnil, nil, msg
. -
lcmark.load_filter(filename)
: Loads a filter from a Lua file (see Filters) and populates the loaded function's environment with all the fields fromcmark-lua
. Returns the filter function on success, ornil, msg
on failure. -
lcmark.compile_template(str)
: Compiles a template stringstr
(see Templates) into an arbitrary template object which can then be passed tolcmark.apply_template()
. Returns the template object on success, ornil, msg
on failure. -
lcmark.apply_template(template, context)
: Renders a compiled template object into a string using a context table (typically the document's metadata). -
lcmark.render_template(str, context)
: Compiles and applies a template string to a context table. Returns the resulting document string on success, ornil, msg
on failure. -
lcmark.writers
: a table with strings as keys (html
,latex
,man
,xml
,commonmark
) and renderers as values. A renderer is a function that takes three arguments (a cmark node, cmark options (a number), and a column width (a number). It returns the rendered output as a string.
Development
make
builds the rock and installs it locally.
make test
runs some tests. These are in test.t
and tests/
.
You'll need the prove
executable,
as well as luacheck and
lua-TestMore.
make update
will update the spec tests from the
../cmark
directory.
make -C standalone
will create a fully self-contained version
of lcmark
which embeds the lua interpreter and all required
libraries, resulting in no external dependencies.