Earmark v1.2.4 Earmark.Plugin
Plugins are modules that implement a render function. Right now that is as_html
.
API
Plugin Registration
When invoking Earmark.as_html(some_md, options)
we can register plugins inside the plugins
map, where
each plugin is a value pointed to by the prefix triggering it.
Prefixes are appended to "$$"
and lines starting by that string will be rendered by the registered plugin.
%Earmark.Options{plugins: %{"" => CommentPlugin}}
would trigger the CommentPlugin
for each block of
lines prefixed by $$
, while %Earmark.Options{plugins: %{"cp" => CommentPlugin}}
would do the same for
blocks of lines prefixed by $$cp
.
Please see the documentation of Plugin.define
for a convenience function that helps creating the necessary
Earmark.Options
structs for the usage of plugins.
Plugin Invocation
as_html
(or other render functions in the future) is invoked with a list of pairs containing the text
and line number of the lines in the block. As an example, if our plugin was registered with the default prefix
of ""
and the markdown to be converted was:
# Plugin output ahead
$$ line one
$$
$$ line two
as_html
would be invoked as follows:
as_html([{"line one", 2}, {"", 3}, {"line two", 4})
Plugin Output
Earmark’s render function will invoke the plugin’s render function as explained above. It can then integrate the return value of the function into the generated rendering output if it complies to the following criteria.
- It returns a string
- It returns a list of strings
- It returns a pair of lists containing a list of strings and a list of error/warning tuples.
Where the tuples are of the form
{:error | :warning, line_number, descriptive_text}
A complete example
iex> defmodule MyPlug do
...> def as_html(lines) do
...> # to demonstrate the three possible return values
...> case render(lines) do
...> {[line], []} -> line
...> {lines, []} -> lines
...> tuple -> tuple
...> end
...> end
...>
...> defp render(lines) do
...> Enum.map(lines, &render_line/1) |> Enum.partition(&ok?/1)
...> end
...>
...> defp render_line({"", _}), do: "<hr/>"
...> defp render_line({"line one", _}), do: "<p>first line</p>\n"
...> defp render_line({line, lnb}), do: {:error, lnb, line}
...>
...> defp ok?({_, _, _}), do: false
...> defp ok?(_), do: true
...> end
...>
...> lines = [
...> "# Plugin Ahead",
...> "$$ line one",
...> "$$",
...> "$$ line two",
...> ]
...> Earmark.as_html(lines, Earmark.Plugin.define(MyPlug))
{:error, "<h1>Plugin Ahead</h1>\n<p>first line</p>\n<hr/>", [{ :error, 4, "line two"}]}
Plugins, reusing Earmark
As long as you avoid endless recursion there is absolutely no problem to call Earmark.as_html
in your plugin, consider the following
example in which the plugin will parse markdown and render html verbatim (which uis stupid, that is what Earmark already does for you,
but just to demonstrate the possibilities):
iex> defmodule Again do
...> def as_html(lines) do
...> text_lines = Enum.map(lines, fn {str, _} -> str end)
...> {_, html, errors} = Earmark.as_html(text_lines)
...> { Enum.join([html | text_lines]), errors }
...> end
...> end
...> lines = [
...> "$$a * one",
...> "$$a * two",
...> ]
...> Earmark.as_html(lines, Earmark.Plugin.define({Again, "a"}))
{:ok, "<ul>\n<li>one\n</li>\n<li>two\n</li>\n</ul>\n* one* two", []}
Link to this section Summary
Functions
adds the definition of one or more plugins to Earmark.Options
Link to this section Functions
adds the definition of one or more plugins to Earmark.Options
.
If the plugin is defined with the default prefix and no other options are needed one can use the one parameter form:
iex> Earmark.Plugin.define(Earmark) # not a legal plugin of course
%Earmark.Options{plugins: %{"" => Earmark}}
More then one plugin can be defined, as long as all prefixes differ
iex> defmodule P1, do: nil
...> defmodule P2, do: nil
...> Earmark.Plugin.define([ Earmark, {P1, "p1"}, {P2, "p2"} ])
%Earmark.Options{plugins: %{"" => Earmark, "p1" => Unit.PluginTest.P1, "p2" => Unit.PluginTest.P2}}