EarmarkAstDsl

CICoverage StatusHex.pmHex.pmHex.pm

EarmarkAstDsl is a toolset to generate EarmarkParser conformant AST Nodes version 1.4.6 and on, which is the always return quadruples version.

Its main purpose is to remove boilerplate code from Earmark and EarmarkParser tests. Documentation for EarmarkAstDsl.

tag

The most general helper is the tag function:

    iex(1)> tag("div", "Some content")
    {"div", [], ["Some content"], %{}}

Content and attributes can be provided as arrays, ...

    iex(2)> tag("p", ~w[Hello World], class: "hidden")
    {"p", [{"class", "hidden"}], ["Hello", "World"], %{}}

... or maps:

    iex(3)> tag("p", ~w[Hello World], %{class: "hidden"})
    {"p", [{"class", "hidden"}], ["Hello", "World"], %{}}

Annotations (as implemented in EarmarkParser >= v1.4.16)

In order to not overload tha API for tag/2, tag/3 and tag/4 we offer the general tag_annotated function

    iex(4)> tag_annotated("header", "content", "annotation")
    {"header", [], ["content"], %{annotation: "annotation"}}

in this case atts come last

    iex(5)> tag_annotated("header", "content", "annotation", class: "two")
    {"header", [{"class", "two"}], ["content"], %{annotation: "annotation"}}

Shortcuts for common tags (a, div, li, p)

    iex(6)> a("hello", href: "mylink")
    {"a", [{"href", "mylink"}], ["hello"], %{}}
    iex(7)> p("A para")
    {"p", [], ["A para"], %{}}
    iex(8)> div(tag("span", "content"))
    {"div", [], [{"span", [], ["content"], %{}}], %{}}
    iex(9)> li(p("hello"))
    {"li", [], [{"p", [], ["hello"], %{}}], %{}}
    iex(10)> a("world", [href: "alink"], %{wikilinks: true})
    {"a", [{"href", "alink"}], ["world"], %{wikilinks: true}}

Pluriel form for paragraphs tags

    iex(11)> tags("p", ~W[a b c])
    [{"p", [], ["a"], %{}},
      {"p", [], ["b"], %{}},
      {"p", [], ["c"], %{}}]

More helpers, which are less common are described on their functiondocs

EarmarkAstDsl.blockquote/2

A convenient shortcut for the often occurring <blockquoye><p> tag chain

    iex(12)> blockquote("Importante!")
    {"blockquote", [], [{"p", [], ["Importante!"], %{}}], %{}}

All passed in attributes go to the blockquote tag

    iex(13)> blockquote("Très important", lang: "fr")
    {"blockquote", [{"lang", "fr"}], [{"p", [], ["Très important"], %{}}], %{}}

EarmarkAstDsl.div_annotated/3

    iex(14)> div_annotated("content", "special", class: "special_class")
    {"div", [{"class", "special_class"}], ["content"], %{annotation: "special"}}
    iex(15)> div_annotated("content", "special")
    {"div", [], ["content"], %{annotation: "special"}}

EarmarkAstDsl.inline_code/2

    iex(16)> inline_code("with x <- great_value() do")
    {"code", [{"class", "inline"}], ["with x <- great_value() do"], %{}}

EarmarkAstDsl.lis/1

Creates a list of li itmes

    iex(17)> lis(~W[alpha beta gamma])
    [{"li", [], ["alpha"], %{}},
    {"li", [], ["beta"], %{}},
    {"li", [], ["gamma"], %{}}]

Which can typically be used in, well, a list

    iex(18)> tag("ol", lis(["a", p("b")]))
    {"ol", [], [{"li", [], ["a"], %{}}, {"li", [], [{"p", [], ["b"], %{}}], %{}}], %{}}

EarmarkAstDsl.ol/2

The ol helper is different in respect to other helpers as it wraps content elements into li tags if necessary

    iex(19)> ol(["hello", "world"])
    {"ol", [], [{"li", [], ["hello"], %{}}, {"li", [], ["world"], %{}}], %{}}

but as mentioned only if necessary so that we can refine li elements with attributes or meta if we want

    iex(20)> ol(["hello", li("world", class: "global")])
    {"ol", [], [{"li", [], ["hello"], %{}}, {"li", [{"class", "global"}], ["world"], %{}}], %{}}

if there is only one li no list needs to be passed in

    iex(21)> ol("hello")
    {"ol", [], [{"li", [], ["hello"], %{}}], %{}}

EarmarkAstDsl.p_annotated/3

    iex(22)> p_annotated("text", "annotation")
    {"p", [], ["text"], %{annotation: "annotation"}}

EarmarkAstDsl.pre_code/2

A convenient shortcut for the often occurring <pre><code> tag chain

    iex(23)> pre_code("hello")
    {"pre", [], [{"code", [], ["hello"], %{}}], %{}}

EarmarkAstDsl.pre_code_annotated/3

The annotation adding helper

    iex(24)> pre_code_annotated("code", "@@lang=elixir")
    {"pre", [], [{"code", [], ["code"], %{annotation: "@@lang=elixir"}}], %{}}

EarmarkAstDsl.table/2

Tables

Tables are probably the raison d'être ot this little lib, as their ast is quite verbose, as we will see here:

    iex(28)> table("one cell only") # and look at the output
    {"table", [], [
      {"tbody", [], [
        {"tr", [], [
          {"td", [{"style", "text-align: left;"}], ["one cell only"], %{}}
        ], %{}}
      ], %{}}
    ], %{}}

Now if we want a header and have some more data:

    iex(29)> table([~w[1-1 1-2], ~w[2-1 2-2]], head: ~w[left right]) # This is quite verbose!
    {"table", [], [
      {"thead", [], [
        {"tr", [], [
          {"th", [{"style", "text-align: left;"}], ["left"], %{}},
          {"th", [{"style", "text-align: left;"}], ["right"], %{}},
        ], %{}}
      ], %{}},
      {"tbody", [], [
        {"tr", [], [
          {"td", [{"style", "text-align: left;"}], ["1-1"], %{}},
          {"td", [{"style", "text-align: left;"}], ["1-2"], %{}},
        ], %{}},
        {"tr", [], [
          {"td", [{"style", "text-align: left;"}], ["2-1"], %{}},
          {"td", [{"style", "text-align: left;"}], ["2-2"], %{}},
        ], %{}}
      ], %{}}
    ], %{}}

And tables can easily be aligned differently in Markdown, which makes some style helpers very useful

    iex(30)> table([~w[1-1 1-2], ~w[2-1 2-2]],
    ...(30)>        head: ~w[alpha beta],
    ...(30)>        text_aligns: ~w[right center])
    {"table", [], [
      {"thead", [], [
        {"tr", [], [
          {"th", [{"style", "text-align: right;"}], ["alpha"], %{}},
          {"th", [{"style", "text-align: center;"}], ["beta"], %{}},
        ], %{}}
      ], %{}},
      {"tbody", [], [
        {"tr", [], [
          {"td", [{"style", "text-align: right;"}], ["1-1"], %{}},
          {"td", [{"style", "text-align: center;"}], ["1-2"], %{}},
        ], %{}},
        {"tr", [], [
          {"td", [{"style", "text-align: right;"}], ["2-1"], %{}},
          {"td", [{"style", "text-align: center;"}], ["2-2"], %{}},
        ], %{}}
      ], %{}}
    ], %{}}

Some leeway is given for the determination of the number of columns, bear in mind that Markdown only supports regularly shaped tables with a fixed number of columns.

Problems might arise when we have a table like the following

      | alpha        |
      | beta *gamma* |

where the first cell contains one element, but the second two, we can hint that we only want one by grouping into tuples

      iex(31)> table(["alpha", {"beta", tag("em", "gamma")}])
      {"table", [], [
        {"tbody", [], [
          {"tr", [], [
            {"td", [{"style", "text-align: left;"}], ["alpha"], %{}},
          ], %{}},
          {"tr", [], [
            {"td", [{"style", "text-align: left;"}], ["beta", {"em", [], ["gamma"], %{}}], %{}}
          ], %{}}
        ], %{}}
      ], %{}}

EarmarkAstDsl.tag/4

This is the base helper which emits a tag with its content, attributes and metadata can be added at the user's convenience

      iex(32)> tag("div")
      {"div", [], [], %{}}

With content,

      iex(33)> tag("span", "hello")
      {"span", [], ["hello"], %{}}

... and attributes,

      iex(34)> tag("code", "let it(:be_light)", [class: "inline"])
      {"code", [{"class", "inline"}], ["let it(:be_light)"], %{}}

... and metadata

      iex(35)> tag("div", "content", [], %{verbatim: true})
      {"div", [], ["content"], %{verbatim: true}}

EarmarkAstDsl.tag_annotated/4

A convience function for easy addition of an annotation to the meta map

EarmarkAstDsl.ul/2

The ul helper is different in respect to other helpers as it wraps content elements into li tags if necessary

    iex(25)> ul(["hello", "world"])
    {"ul", [], [{"li", [], ["hello"], %{}}, {"li", [], ["world"], %{}}], %{}}

but as mentioned only if necessary so that we can refine li elements with attributes or meta if we want

    iex(26)> ul(["hello", li("world", class: "global")])
    {"ul", [], [{"li", [], ["hello"], %{}}, {"li", [{"class", "global"}], ["world"], %{}}], %{}}

if there is only one li no list needs to be passed in

    iex(27)> ul("hello")
    {"ul", [], [{"li", [], ["hello"], %{}}] , %{}}

EarmarkAstDsl.void_tag/2

Void tags are just convenient shortcats for calls to tag with the second argument nil or []

One cannot pass metadata to a void_tag call

      iex(36)> void_tag("hr")
      {"hr", [], [], %{}}
      iex(37)> void_tag("hr", class: "thin")
      {"hr", [{"class", "thin"}], [], %{}}

EarmarkAstDsl.void_tag_annotated/3

Again the annotated version is available

      iex(38)> void_tag_annotated("br", "// break")
      {"br", [], [], %{annotation: "// break"}}
      iex(39)> void_tag_annotated("wbr", "// for printer", class: "nine")
      {"wbr", [{"class", "nine"}], [], %{annotation: "// for printer"}}

EarmarkAstDsl.vtag/3

vtags are tags from verbatim html

      iex(40)> vtag("div", "hello")
      {"div", [], ["hello"], %{verbatim: true}}

Attributes can be provided, of course

      iex(41)> vtag("div", ["some", "content"], [{"data-lang", "elixir"}])
      {"div", [{"data-lang", "elixir"}], ["some", "content"], %{verbatim: true}}

EarmarkAstDsl.vtag_annotated/4

Verbatim tags still can be annotated and therefore we have this helper

    iex(42)> vtag_annotated("i", "emphasized", "-- verbatim", printer: "no")
    {"i", [{"printer", "no"}], ["emphasized"], %{annotation: "-- verbatim", verbatim: true}}

Same doc can be found here (for latest release)

https://hexdocs.pm/earmark_ast_dsl.

Author

Copyright © 2020,1,2 Robert Dober mailto: robert.dober@gmail.com

LICENSE

Apache License 2.0 LICENSE for details.