zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

README.md (22661B)


      1 
      2 # EarmarkParser A Pure Elixir Markdown Parser
      3 
      4 [![CI](https://github.com/robertdober/earmark_parser/workflows/CI/badge.svg)](https://github.com/robertdober/earmark_parser/actions)
      5 [![Coverage Status](https://coveralls.io/repos/github/RobertDober/earmark_parser/badge.svg?branch=master)](https://coveralls.io/github/RobertDober/earmark_parser?branch=master)
      6 [![Hex.pm](https://img.shields.io/hexpm/v/earmark_parser.svg)](https://hex.pm/packages/earmark_parser)
      7 [![Hex.pm](https://img.shields.io/hexpm/dw/earmark_parser.svg)](https://hex.pm/packages/earmark_parser)
      8 [![Hex.pm](https://img.shields.io/hexpm/dt/earmark_parser.svg)](https://hex.pm/packages/earmark_parser)
      9 
     10 
     11 ## Table Of Contents
     12 
     13 - [Table Of Contents](#table-of-contents)
     14 - [Usage](#usage)
     15   - [EarmarkParser](#earmarkparser)
     16   - [API](#api)
     17     - [EarmarkParser.as_ast](#earmarkparseras_ast)
     18     - [Options](#options)
     19 - [Supports](#supports)
     20 - [Extensions](#extensions)
     21   - [Links](#links)
     22     - [Links supported by default](#links-supported-by-default)
     23     - [Autolinks](#autolinks)
     24     - [Additional link parsing via options](#additional-link-parsing-via-options)
     25     - [Pure links](#pure-links)
     26     - [Wikilinks...](#wikilinks)
     27   - [Sub and Sup HTML Elements](#sub-and-sup-html-elements)
     28   - [Github Flavored Markdown](#github-flavored-markdown)
     29     - [Strike Through](#strike-through)
     30     - [GFM Tables](#gfm-tables)
     31     - [Syntax Highlighting](#syntax-highlighting)
     32     - [Footnotes](#footnotes)
     33     - [Breaks](#breaks)
     34     - [Enabling **all** options that are disabled by default](#enabling-all-options-that-are-disabled-by-default)
     35     - [Tables](#tables)
     36     - [HTML Blocks](#html-blocks)
     37     - [HTML Comments](#html-comments)
     38     - [Lists](#lists)
     39   - [Adding Attributes with the IAL extension](#adding-attributes-with-the-ial-extension)
     40     - [To block elements](#to-block-elements)
     41     - [To links or images](#to-links-or-images)
     42 - [Limitations](#limitations)
     43 - [Annotations](#annotations)
     44   - [Annotated Paragraphs](#annotated-paragraphs)
     45   - [Annotated HTML elements](#annotated-html-elements)
     46   - [Commenting your Markdown](#commenting-your-markdown)
     47   - [EarmarkParser.as_ast/2](#earmarkparseras_ast2)
     48   - [EarmarkParser.version/0](#earmarkparserversion0)
     49 - [Contributing](#contributing)
     50 - [Author](#author)
     51 - [LICENSE](#license)
     52 
     53 ## Usage
     54 
     55 ### EarmarkParser
     56 
     57 
     58 ### API
     59 
     60 #### EarmarkParser.as_ast
     61 
     62 This is the structure of the result of `as_ast`.
     63 
     64     {:ok, ast, []}                   = EarmarkParser.as_ast(markdown)
     65     {:ok, ast, deprecation_messages} = EarmarkParser.as_ast(markdown)
     66     {:error, ast, error_messages}    = EarmarkParser.as_ast(markdown)
     67 
     68 For examples see the functiondoc below.
     69 
     70 #### Options
     71 
     72 Options can be passed into `as_ast/2` according to the documentation of `EarmarkParser.Options`.
     73 
     74     {status, ast, errors} = EarmarkParser.as_ast(markdown, options)
     75 
     76 ## Supports
     77 
     78 Standard [Gruber markdown][gruber].
     79 
     80 [gruber]: <http://daringfireball.net/projects/markdown/syntax>
     81 
     82 ## Extensions
     83 
     84 ### Links
     85 
     86 #### Links supported by default
     87 
     88 ##### Oneline HTML Link tags
     89 
     90 ```elixir
     91     iex(1)> EarmarkParser.as_ast(~s{<a href="href">link</a>})
     92     {:ok, [{"a", [{"href", "href"}], ["link"], %{verbatim: true}}], []}
     93 ```
     94 
     95 ##### Markdown links
     96 
     97 New style ...
     98 
     99 ```elixir
    100     iex(2)> EarmarkParser.as_ast(~s{[title](destination)})
    101     {:ok,  [{"p", [], [{"a", [{"href", "destination"}], ["title"], %{}}], %{}}], []}
    102 ```
    103 
    104 and old style
    105 
    106 ```elixir
    107     iex(3)> EarmarkParser.as_ast("[foo]: /url \"title\"\n\n[foo]\n")
    108     {:ok, [{"p", [], [{"a", [{"href", "/url"}, {"title", "title"}], ["foo"], %{}}], %{}}], []}
    109 ```
    110 
    111 #### Autolinks
    112 
    113 ```elixir
    114     iex(4)> EarmarkParser.as_ast("<https://elixir-lang.com>")
    115     {:ok, [{"p", [], [{"a", [{"href", "https://elixir-lang.com"}], ["https://elixir-lang.com"], %{}}], %{}}], []}
    116 ```
    117 
    118 #### Additional link parsing via options
    119 
    120 
    121 #### Pure links
    122 
    123 **N.B.** that the `pure_links` option is `true` by default
    124 
    125 ```elixir
    126     iex(5)> EarmarkParser.as_ast("https://github.com")
    127     {:ok, [{"p", [], [{"a", [{"href", "https://github.com"}], ["https://github.com"], %{}}], %{}}], []}
    128 ```
    129 
    130 But can be deactivated
    131 
    132 ```elixir
    133     iex(6)> EarmarkParser.as_ast("https://github.com", pure_links: false)
    134     {:ok, [{"p", [], ["https://github.com"], %{}}], []}
    135 ```
    136 
    137 
    138   #### Wikilinks...
    139 
    140   are disabled by default
    141 
    142 ```elixir
    143     iex(7)> EarmarkParser.as_ast("[[page]]")
    144     {:ok, [{"p", [], ["[[page]]"], %{}}], []}
    145 ```
    146 
    147   and can be enabled
    148 
    149 ```elixir
    150     iex(8)> EarmarkParser.as_ast("[[page]]", wikilinks: true)
    151     {:ok, [{"p", [], [{"a", [{"href", "page"}], ["page"], %{wikilink: true}}], %{}}], []}
    152 ```
    153 
    154 
    155 ### Sub and Sup HTML Elements
    156 
    157 This feature is not enabled by default but can be enabled with the option `sub_sup: true`
    158 
    159 Therefore we will get
    160 
    161 ```elixir
    162     iex(9)> EarmarkParser.as_ast("H~2~O or a^n^ + b^n^ = c^n^")
    163     {:ok, [{"p", [], ["H~2~O or a^n^ + b^n^ = c^n^"], %{}}], []}
    164 ```
    165 
    166 But by specifying `sub_sup: true`
    167 
    168 ```elixir
    169     iex(10)> EarmarkParser.as_ast("H~2~O or a^n^ + b^n^ = c^n^", sub_sup: true)
    170     {:ok, [{"p", [], ["H", {"sub", [], ["2"], %{}}, "O or a", {"sup", [], ["n"], %{}}, " + b", {"sup", [], ["n"], %{}}, " = c", {"sup", [], ["n"], %{}}], %{}}], []}
    171 ```
    172 
    173 ### Github Flavored Markdown
    174 
    175 GFM is supported by default, however as GFM is a moving target and all GFM extension do not make sense in a general context, EarmarkParser does not support all of it, here is a list of what is supported:
    176 
    177 #### Strike Through
    178 
    179 ```elixir
    180     iex(11)> EarmarkParser.as_ast("~~hello~~")
    181     {:ok, [{"p", [], [{"del", [], ["hello"], %{}}], %{}}], []}
    182 ```
    183 
    184 #### GFM Tables
    185 
    186 Are not enabled by default
    187 
    188 ```elixir
    189     iex(12)> as_ast("a|b\\n-|-\\nc|d\\n")
    190     {:ok, [{"p", [], ["a|b\\n-|-\\nc|d\\n"], %{}}], []}
    191 ```
    192 
    193 But can be enabled with `gfm_tables: true`
    194 
    195 ```elixir
    196     iex(13)> as_ast("a|b\n-|-\nc|d\n", gfm_tables: true)
    197     {:ok,
    198       [
    199         {
    200           "table",
    201           [],
    202           [
    203             {"thead", [], [{"tr", [], [{"th", [{"style", "text-align: left;"}], ["a"], %{}}, {"th", [{"style", "text-align: left;"}], ["b"], %{}}], %{}}], %{}},
    204             {"tbody", [], [{"tr", [], [{"td", [{"style", "text-align: left;"}], ["c"], %{}}, {"td", [{"style", "text-align: left;"}], ["d"], %{}}], %{}}], %{}}
    205           ],
    206           %{}
    207         }
    208       ],
    209       []}
    210 ```
    211 
    212 #### Syntax Highlighting
    213 
    214 All backquoted or fenced code blocks with a language string are rendered with the given
    215 language as a _class_ attribute of the _code_ tag.
    216 
    217 For example:
    218 
    219 ```elixir
    220     iex(14)> [
    221     ...(14)>    "```elixir",
    222     ...(14)>    " @tag :hello",
    223     ...(14)>    "```"
    224     ...(14)> ] |> as_ast()
    225     {:ok, [{"pre", [], [{"code", [{"class", "elixir"}], [" @tag :hello"], %{}}], %{}}], []}
    226 ```
    227 
    228 will be rendered as shown in the doctest above.
    229 
    230 If you want to integrate with a syntax highlighter with different conventions you can add more classes by specifying prefixes that will be
    231 put before the language string.
    232 
    233 Prism.js for example needs a class `language-elixir`. In order to achieve that goal you can add `language-`
    234 as a `code_class_prefix` to `EarmarkParser.Options`.
    235 
    236 In the following example we want more than one additional class, so we add more prefixes.
    237 
    238 ```elixir
    239     iex(15)> [
    240     ...(15)>    "```elixir",
    241     ...(15)>    " @tag :hello",
    242     ...(15)>    "```"
    243     ...(15)> ] |> as_ast(%EarmarkParser.Options{code_class_prefix: "lang- language-"})
    244     {:ok, [{"pre", [], [{"code", [{"class", "elixir lang-elixir language-elixir"}], [" @tag :hello"], %{}}], %{}}], []}
    245 ```
    246 
    247 
    248 #### Footnotes
    249 
    250 **N.B.** Footnotes are disabled by default, use `as_ast(..., footnotes: true)` to enable them
    251 
    252 Footnotes are now a **superset** of GFM Footnotes. This implies some changes
    253 
    254   - Footnote definitions (`[^footnote_id]`) must come at the end of your document (_GFM_)
    255   - Footnotes that are not referenced are not rendered anymore (_GFM_)
    256   - Footnote definitions can contain any markup with the exception of footnote definitions
    257 
    258 ```elixir
    259     iex(16)> markdown = [
    260     ...(16)> "My reference[^to_footnote]",
    261     ...(16)> "",
    262     ...(16)> "[^1]: I am not rendered",
    263     ...(16)> "[^to_footnote]: Important information"]
    264     ...(16)> {:ok, ast, []} = as_ast(markdown, footnotes: true)
    265     ...(16)> ast
    266     [
    267       {"p", [], ["My reference",
    268         {"a",
    269          [{"href", "#fn:to_footnote"}, {"id", "fnref:to_footnote"}, {"class", "footnote"}, {"title", "see footnote"}],
    270          ["to_footnote"], %{}}
    271       ], %{}},
    272       {"div",
    273        [{"class", "footnotes"}],
    274        [{"hr", [], [], %{}},
    275         {"ol", [],
    276          [{"li", [{"id", "fn:to_footnote"}],
    277            [{"a", [{"class", "reversefootnote"}, {"href", "#fnref:to_footnote"}, {"title", "return to article"}], ["&#x21A9;"], %{}},
    278             {"p", [], ["Important information"], %{}}], %{}}
    279         ], %{}}], %{}}
    280     ]
    281 ```
    282 
    283   For more complex examples of footnotes, please refer to
    284   [these tests](https://github.com/RobertDober/earmark_parser/tree/master/test/acceptance/ast/footnotes/multiple_fn_test.exs)
    285 
    286 #### Breaks
    287 
    288     Hard linebreaks are disabled by default
    289 
    290 ```elixir
    291         iex(17)> ["* a","  b", "c"]
    292         ...(17)> |> as_ast()
    293         {:ok,
    294           [{"ul", [], [{"li", [], ["a\nb\nc"], %{}}], %{}}],
    295           []}
    296 ```
    297 
    298     But can be enabled with `breaks: true`
    299 
    300 ```elixir
    301         iex(18)> ["* a","  b", "c"]
    302         ...(18)> |> as_ast(breaks: true)
    303         {:ok, [{"ul", [], [{"li", [], ["a", {"br", [], [], %{}}, "b", {"br", [], [], %{}}, "c"], %{}}], %{}}], []}
    304 ```
    305 
    306 #### Enabling **all** options that are disabled by default
    307 
    308     Can be achieved with the `all: true` option
    309 
    310 ```elixir
    311         iex(19)> [
    312         ...(19)> "a^n^",
    313         ...(19)> "b~2~",
    314         ...(19)> "[[wikilink]]"]
    315         ...(19)> |> as_ast(all: true)
    316         {:ok, [
    317           {"p", [], ["a", {"sup", [], ["n"], %{}}, {"br", [], [], %{}}, "b", {"sub", [], ["2"], %{}}, {"br", [], [], %{}}, {"a", [{"href", "wikilink"}], ["wikilink"], %{wikilink: true}}], %{}}
    318           ],
    319           []}
    320 ```
    321 
    322 #### Tables
    323 
    324 Are supported as long as they are preceded by an empty line.
    325 
    326     State | Abbrev | Capital
    327     ----: | :----: | -------
    328     Texas | TX     | Austin
    329     Maine | ME     | Augusta
    330 
    331 Tables may have leading and trailing vertical bars on each line
    332 
    333     | State | Abbrev | Capital |
    334     | ----: | :----: | ------- |
    335     | Texas | TX     | Austin  |
    336     | Maine | ME     | Augusta |
    337 
    338 Tables need not have headers, in which case all column alignments
    339 default to left.
    340 
    341     | Texas | TX     | Austin  |
    342     | Maine | ME     | Augusta |
    343 
    344 Currently we assume there are always spaces around interior vertical unless
    345 there are exterior bars.
    346 
    347 However in order to be more GFM compatible the `gfm_tables: true` option
    348 can be used to interpret only interior vertical bars as a table if a separation
    349 line is given, therefore
    350 
    351      Language|Rating
    352      --------|------
    353      Elixir  | awesome
    354 
    355 is a table (if and only if `gfm_tables: true`) while
    356 
    357      Language|Rating
    358      Elixir  | awesome
    359 
    360 never is.
    361 
    362 #### HTML Blocks
    363 
    364 HTML is not parsed recursively or detected in all conditions right now, though GFM compliance
    365 is a goal.
    366 
    367 But for now the following holds:
    368 
    369 A HTML Block defined by a tag starting a line and the same tag starting a different line is parsed
    370 as one HTML AST node, marked with %{verbatim: true}
    371 
    372 E.g.
    373 
    374 ```elixir
    375     iex(20)> lines = [ "<div><span>", "some</span><text>", "</div>more text" ]
    376     ...(20)> EarmarkParser.as_ast(lines)
    377     {:ok, [{"div", [], ["<span>", "some</span><text>"], %{verbatim: true}}, "more text"], []}
    378 ```
    379 
    380 And a line starting with an opening tag and ending with the corresponding closing tag is parsed in similar
    381 fashion
    382 
    383 ```elixir
    384     iex(21)> EarmarkParser.as_ast(["<span class=\"superspan\">spaniel</span>"])
    385     {:ok, [{"span", [{"class", "superspan"}], ["spaniel"], %{verbatim: true}}], []}
    386 ```
    387 
    388 What is HTML?
    389 
    390 We differ from strict GFM by allowing **all** tags not only HTML5 tags this holds for one liners....
    391 
    392 ```elixir
    393     iex(22)> {:ok, ast, []} = EarmarkParser.as_ast(["<stupid />", "<not>better</not>"])
    394     ...(22)> ast
    395     [
    396       {"stupid", [], [], %{verbatim: true}},
    397       {"not", [], ["better"], %{verbatim: true}}]
    398 ```
    399 
    400 and for multi line blocks
    401 
    402 ```elixir
    403     iex(23)> {:ok, ast, []} = EarmarkParser.as_ast([ "<hello>", "world", "</hello>"])
    404     ...(23)> ast
    405     [{"hello", [], ["world"], %{verbatim: true}}]
    406 ```
    407 
    408 #### HTML Comments
    409 
    410 Are recognized if they start a line (after ws and are parsed until the next `-->` is found
    411 all text after the next '-->' is ignored
    412 
    413 E.g.
    414 
    415 ```elixir
    416     iex(24)> EarmarkParser.as_ast(" <!-- Comment\ncomment line\ncomment --> text -->\nafter")
    417     {:ok, [{:comment, [], [" Comment", "comment line", "comment "], %{comment: true}}, {"p", [], ["after"], %{}}], []}
    418 ```
    419 
    420 
    421 #### Lists
    422 
    423 Lists are pretty much GFM compliant, but some behaviors concerning the interpreation of the markdown inside a List Item's first
    424 paragraph seem not worth to be interpreted, examples are blockquote in a tight [list item](ttps://babelmark.github.io/?text=*+aa%0A++%3E+Second)
    425 which we can only have in a [loose one](https://babelmark.github.io/?text=*+aa%0A++%0A++%3E+Second)
    426 
    427 Or a headline in a [tight list item](https://babelmark.github.io/?text=*+bb%0A++%23+Headline) which, again is only available in the
    428 [loose version](https://babelmark.github.io/?text=*+bb%0A%0A++%23+Headline) in EarmarkParser.
    429 
    430 furthermore [this example](https://babelmark.github.io/?text=*+aa%0A++%60%60%60%0ASecond%0A++%60%60%60) demonstrates how weird
    431 and definitely not useful GFM's own interpretation can get.
    432 
    433 Therefore we stick to a more predictable approach.
    434 
    435 ```elixir
    436       iex(25)> markdown = [
    437       ...(25)> "* aa",
    438       ...(25)> "  ```",
    439       ...(25)> "Second",
    440       ...(25)> "  ```" ]
    441       ...(25)> as_ast(markdown)
    442       {:ok, [{"ul", [], [{"li", [], ["aa", {"pre", [], [{"code", [], ["Second"], %{}}], %{}}], %{}}], %{}}], []}
    443 ```
    444 
    445 Also we do support the immediate style of block content inside lists
    446 
    447 ```elixir
    448       iex(26)> as_ast("* > Nota Bene!")
    449       {:ok, [{"ul", [], [{"li", [], [{"blockquote", [], [{"p", [], ["Nota Bene!"], %{}}], %{}}], %{}}], %{}}], []}
    450 ```
    451 
    452 or
    453 
    454 ```elixir
    455       iex(27)> as_ast("1. # Breaking...")
    456       {:ok, [{"ol", [], [{"li", [], [{"h1", [], ["Breaking..."], %{}}], %{}}], %{}}], []}
    457 ```
    458 
    459 
    460 ### Adding Attributes with the IAL extension
    461 
    462 #### To block elements
    463 
    464 HTML attributes can be added to any block-level element. We use
    465 the Kramdown syntax: add the line `{:` _attrs_ `}` following the block.
    466 
    467 ```elixir
    468     iex(28)> markdown = ["# Headline", "{:.from-next-line}"]
    469     ...(28)> as_ast(markdown)
    470     {:ok, [{"h1", [{"class", "from-next-line"}], ["Headline"], %{}}], []}
    471 ```
    472 
    473 Headers can also have the IAL string at the end of the line
    474 
    475 ```elixir
    476     iex(29)> markdown = ["# Headline{:.from-same-line}"]
    477     ...(29)> as_ast(markdown)
    478     {:ok, [{"h1", [{"class", "from-same-line"}], ["Headline"], %{}}], []}
    479 ```
    480 
    481 A special use case is headers inside blockquotes which allow for some nifty styling in `ex_doc`*
    482 see [this PR](https://github.com/elixir-lang/ex_doc/pull/1400) if you are interested in the technical
    483 details
    484 
    485 ```elixir
    486     iex(30)> markdown = ["> # Headline{:.warning}"]
    487     ...(30)> as_ast(markdown)
    488     {:ok, [{"blockquote", [], [{"h1", [{"class", "warning"}], ["Headline"], %{}}], %{}}], []}
    489 ```
    490 
    491 This also works for headers inside lists
    492 
    493 ```elixir
    494     iex(31)> markdown = ["- # Headline{:.warning}"]
    495     ...(31)> as_ast(markdown)
    496     {:ok, [{"ul", [], [{"li", [], [{"h1", [{"class", "warning"}], ["Headline"], %{}}], %{}}], %{}}], []}
    497 ```
    498 
    499 It still works for inline code, as it did before
    500 
    501 ```elixir
    502     iex(32)> markdown = "`Enum.map`{:lang=elixir}"
    503     ...(32)> as_ast(markdown)
    504     {:ok, [{"p", [], [{"code", [{"class", "inline"}, {"lang", "elixir"}], ["Enum.map"], %{}}], %{}}], []}
    505 ```
    506 
    507 
    508 _attrs_ can be one or more of:
    509 
    510   * `.className`
    511   * `#id`
    512   * name=value, name="value", or name='value'
    513 
    514 For example:
    515 
    516     # Warning
    517     {: .red}
    518 
    519     Do not turn off the engine
    520     if you are at altitude.
    521     {: .boxed #warning spellcheck="true"}
    522 
    523 #### To links or images
    524 
    525 It is possible to add IAL attributes to generated links or images in the following
    526 format.
    527 
    528 ```elixir
    529     iex(33)> markdown = "[link](url) {: .classy}"
    530     ...(33)> EarmarkParser.as_ast(markdown)
    531     { :ok, [{"p", [], [{"a", [{"class", "classy"}, {"href", "url"}], ["link"], %{}}], %{}}], []}
    532 ```
    533 
    534 For both cases, malformed attributes are ignored and warnings are issued.
    535 
    536 ```elixir
    537     iex(34)> [ "Some text", "{:hello}" ] |> Enum.join("\n") |> EarmarkParser.as_ast()
    538     {:error, [{"p", [], ["Some text"], %{}}], [{:warning, 2,"Illegal attributes [\"hello\"] ignored in IAL"}]}
    539 ```
    540 
    541 It is possible to escape the IAL in both forms if necessary
    542 
    543 ```elixir
    544     iex(35)> markdown = "[link](url)\\{: .classy}"
    545     ...(35)> EarmarkParser.as_ast(markdown)
    546     {:ok, [{"p", [], [{"a", [{"href", "url"}], ["link"], %{}}, "{: .classy}"], %{}}], []}
    547 ```
    548 
    549 This of course is not necessary in code blocks or text lines
    550 containing an IAL-like string, as in the following example
    551 
    552 ```elixir
    553     iex(36)> markdown = "hello {:world}"
    554     ...(36)> EarmarkParser.as_ast(markdown)
    555     {:ok, [{"p", [], ["hello {:world}"], %{}}], []}
    556 ```
    557 
    558 ## Limitations
    559 
    560   * Block-level HTML is correctly handled only if each HTML
    561     tag appears on its own line. So
    562 
    563         <div>
    564         <div>
    565         hello
    566         </div>
    567         </div>
    568 
    569     will work. However. the following won't
    570 
    571         <div>
    572         hello</div>
    573 
    574   * John Gruber's tests contain an ambiguity when it comes to
    575     lines that might be the start of a list inside paragraphs.
    576 
    577     One test says that
    578 
    579         This is the text
    580         * of a paragraph
    581         that I wrote
    582 
    583     is a single paragraph. The "*" is not significant. However, another
    584     test has
    585 
    586         *   A list item
    587             * an another
    588 
    589     and expects this to be a nested list. But, in reality, the second could just
    590     be the continuation of a paragraph.
    591 
    592     I've chosen always to use the second interpretation—a line that looks like
    593     a list item will always be a list item.
    594 
    595   * Rendering of block and inline elements.
    596 
    597     Block or void HTML elements that are at the absolute beginning of a line end
    598     the preceding paragraph.
    599 
    600     Thusly
    601 
    602         mypara
    603         <hr />
    604 
    605     Becomes
    606 
    607         <p>mypara</p>
    608         <hr />
    609 
    610     While
    611 
    612         mypara
    613          <hr />
    614 
    615     will be transformed into
    616 
    617         <p>mypara
    618          <hr /></p>
    619 
    620 ## Annotations
    621 
    622 **N.B.** this is an experimental feature from v1.4.16-pre on and might change or be removed again
    623 
    624 The idea is that each markdown line can be annotated, as such annotations change the semantics of Markdown
    625 they have to be enabled with the `annotations` option.
    626 
    627 If the `annotations` option is set to a string (only one string is supported right now, but a list might
    628 be implemented later on, hence the name), the last occurrence of that string in a line and all text following
    629 it will be added to the line as an annotation.
    630 
    631 Depending on how that line will eventually be parsed, this annotation will be added to the meta map (the 4th element
    632 in an AST quadruple) with the key `:annotation`
    633 
    634 In the current version the annotation will only be applied to verbatim HTML tags and paragraphs
    635 
    636 Let us show some examples now:
    637 
    638 ### Annotated Paragraphs
    639 
    640 ```elixir
    641     iex(37)> as_ast("hello %> annotated", annotations: "%>")
    642     {:ok, [{"p", [], ["hello "], %{annotation: "%> annotated"}}], []}
    643 ```
    644 
    645 If we annotate more than one line in a para the first annotation takes precedence
    646 
    647 ```elixir
    648     iex(38)> as_ast("hello %> annotated\nworld %> discarded", annotations: "%>")
    649     {:ok, [{"p", [], ["hello \nworld "], %{annotation: "%> annotated"}}], []}
    650 ```
    651 
    652 ### Annotated HTML elements
    653 
    654 In one line
    655 
    656 ```elixir
    657     iex(39)> as_ast("<span>One Line</span> // a span", annotations: "//")
    658     {:ok, [{"span", [], ["One Line"], %{annotation: "// a span", verbatim: true}}], []}
    659 ```
    660 
    661 or block elements
    662 
    663 ```elixir
    664     iex(40)> [
    665     ...(40)> "<div> : annotation",
    666     ...(40)> "  <span>text</span>",
    667     ...(40)> "</div> : discarded"
    668     ...(40)> ] |> as_ast(annotations: " : ")
    669     {:ok, [{"div", [], ["  <span>text</span>"], %{annotation: " : annotation", verbatim: true}}], []}
    670 ```
    671 
    672 ### Commenting your Markdown
    673 
    674 Although many markdown elements do not support annotations yet, they can be used to comment your markdown, w/o cluttering
    675 the generated AST with comments
    676 
    677 ```elixir
    678     iex(41)> [
    679     ...(41)> "# Headline --> first line",
    680     ...(41)> "- item1 --> a list item",
    681     ...(41)> "- item2 --> another list item",
    682     ...(41)> "",
    683     ...(41)> "<http://somewhere/to/go> --> do not go there"
    684     ...(41)> ] |> as_ast(annotations: "-->")
    685     {:ok, [
    686       {"h1", [], ["Headline"], %{}},
    687       {"ul", [], [{"li", [], ["item1 "], %{}}, {"li", [], ["item2 "], %{}}], %{}},
    688       {"p", [], [{"a", [{"href", "http://somewhere/to/go"}], ["http://somewhere/to/go"], %{}}, " "], %{annotation: "--> do not go there"}}
    689       ], []
    690      }
    691 ```
    692 
    693 
    694 ### EarmarkParser.as_ast/2
    695 
    696     iex(42)> markdown = "My `code` is **best**"
    697     ...(42)> {:ok, ast, []} = EarmarkParser.as_ast(markdown)
    698     ...(42)> ast
    699     [{"p", [], ["My ", {"code", [{"class", "inline"}], ["code"], %{}}, " is ", {"strong", [], ["best"], %{}}], %{}}]
    700 
    701 
    702 
    703 ```elixir
    704     iex(43)> markdown = "```elixir\nIO.puts 42\n```"
    705     ...(43)> {:ok, ast, []} = EarmarkParser.as_ast(markdown, code_class_prefix: "lang-")
    706     ...(43)> ast
    707     [{"pre", [], [{"code", [{"class", "elixir lang-elixir"}], ["IO.puts 42"], %{}}], %{}}]
    708 ```
    709 
    710 **Rationale**:
    711 
    712 The AST is exposed in the spirit of [Floki's](https://hex.pm/packages/floki).
    713 
    714 ### EarmarkParser.version/0
    715 
    716   Accesses current hex version of the `EarmarkParser` application. Convenience for
    717   `iex` usage.
    718 
    719 
    720 
    721 ## Contributing
    722 
    723 Pull Requests are happily accepted.
    724 
    725 Please be aware of one _caveat_ when correcting/improving `README.md`.
    726 
    727 The `README.md` is generated by the mix task `readme` from `README.template` and
    728 docstrings by means of `%moduledoc` or `%functiondoc` directives.
    729 
    730 Please identify the origin of the generated text you want to correct and then
    731 apply your changes there.
    732 
    733 Then issue the mix task `readme`, this is important to have a correctly updated `README.md` after the merge of
    734 your PR.
    735 
    736 Thank you all who have already helped with Earmark/EarmarkParser, your names are duely noted in [RELEASE.md](RELEASE.md).
    737 
    738 ## Author
    739 
    740 Copyright © 2014,5,6,7,8,9;2020 Dave Thomas, The Pragmatic Programmers
    741 @/+pragdave,  dave@pragprog.com
    742 Copyright © 2020 Robert Dober
    743 robert.dober@gmail.com
    744 
    745 ## LICENSE
    746 
    747 Same as Elixir, which is Apache License v2.0. Please refer to [LICENSE](LICENSE) for details.
    748 
    749 <!-- SPDX-License-Identifier: Apache-2.0 -->