zf

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

ast_renderer.ex (6951B)


      1 defmodule EarmarkParser.AstRenderer do
      2   alias EarmarkParser.Block
      3   alias EarmarkParser.Context
      4   alias EarmarkParser.Options
      5 
      6   import Context, only: [clear_value: 1, modify_value: 2, prepend: 2, prepend: 3]
      7 
      8   import EarmarkParser.Ast.Emitter
      9   import EarmarkParser.Ast.Inline, only: [convert: 3]
     10   import EarmarkParser.Helpers.AstHelpers
     11   import EarmarkParser.Ast.Renderer.{HtmlRenderer, FootnoteRenderer, TableRenderer}
     12 
     13   @moduledoc false
     14 
     15   def render(blocks, context = %Context{options: %Options{}}, loose? \\ true) do
     16     _render(blocks, context, loose?)
     17   end
     18 
     19   defp _render(blocks, context, loose?)
     20   defp _render([], context, _loose?), do: context
     21 
     22   defp _render([block | blocks], context, loose?) do
     23     context1 = render_block(block, clear_value(context), loose?)
     24     _render(blocks, prepend(context1, context), loose?)
     25   end
     26 
     27   defp render_block(block, context, loose?)
     28   #############
     29   # Paragraph #
     30   #############
     31   defp render_block(%Block.Para{lnb: lnb, lines: lines, attrs: attrs} = para, context, _loose?) do
     32     context1 = convert(lines, lnb, context)
     33     value = context1.value |> Enum.reverse()
     34 
     35     ast =
     36       emit("p", value, attrs)
     37       |> annotate(para)
     38 
     39     prepend(context, ast, context1)
     40   end
     41 
     42   ########
     43   # Html #
     44   ########
     45   defp render_block(%Block.Html{annotation: annotation, html: html}, context, _loose?) do
     46     render_html_block(html, context, annotation)
     47   end
     48 
     49   defp render_block(%Block.HtmlOneline{annotation: annotation, html: html}, context, _loose?) do
     50     render_html_oneline(html, context, annotation)
     51   end
     52 
     53   defp render_block(%Block.HtmlComment{lines: lines}, context, _loose?) do
     54     lines1 = lines |> Enum.map(&render_html_comment_line/1)
     55     prepend(context, emit(:comment, lines1, [], %{comment: true}))
     56   end
     57 
     58   #########
     59   # Ruler #
     60   #########
     61   defp render_block(%Block.Ruler{type: "-", attrs: attrs}, context, _loose?) do
     62     prepend(context, emit("hr", [], merge_attrs(attrs, %{"class" => "thin"})))
     63   end
     64 
     65   defp render_block(%Block.Ruler{type: "_", attrs: attrs}, context, _loose?) do
     66     prepend(context, emit("hr", [], merge_attrs(attrs, %{"class" => "medium"})))
     67   end
     68 
     69   defp render_block(%Block.Ruler{type: "*", attrs: attrs}, context, _loose?) do
     70     prepend(context, emit("hr", [], merge_attrs(attrs, %{"class" => "thick"})))
     71   end
     72 
     73   ###########
     74   # Heading #
     75   ###########
     76   defp render_block(
     77          %Block.Heading{lnb: lnb, level: level, content: content, attrs: attrs},
     78          context,
     79          _loose?
     80        ) do
     81     context1 = convert(content, lnb, clear_value(context))
     82 
     83     modify_value(
     84       context1,
     85       fn _ ->
     86         [
     87           emit(
     88             "h#{level}",
     89             context1.value |> Enum.reverse(),
     90             attrs)
     91         ]
     92       end
     93     )
     94   end
     95 
     96   ##############
     97   # Blockquote #
     98   ##############
     99   defp render_block(%Block.BlockQuote{blocks: blocks, attrs: attrs}, context, _loose?) do
    100     context1 = render(blocks, clear_value(context))
    101 
    102     modify_value(context1, fn ast ->
    103       [emit("blockquote", ast, attrs)]
    104     end)
    105   end
    106 
    107   #########
    108   # Table #
    109   #########
    110   defp render_block(
    111          %Block.Table{lnb: lnb, header: header, rows: rows, alignments: aligns, attrs: attrs},
    112          context,
    113          _loose?
    114        ) do
    115     {rows_ast, context1} = render_rows(rows, lnb, aligns, context)
    116 
    117     {rows_ast1, context2} =
    118       if header do
    119         {header_ast, context3} = render_header(header, lnb, aligns, context1)
    120         {[header_ast | rows_ast], context3}
    121       else
    122         {rows_ast, context1}
    123       end
    124 
    125     prepend(
    126       clear_value(context2),
    127       emit("table", rows_ast1, attrs)
    128     )
    129   end
    130 
    131   ########
    132   # Code #
    133   ########
    134   defp render_block(
    135          %Block.Code{language: language, attrs: attrs} = block,
    136          context = %Context{options: options},
    137          _loose?
    138        ) do
    139     classes =
    140       if language && language != "",
    141         do: [code_classes(language, options.code_class_prefix)],
    142         else: []
    143 
    144     lines = render_code(block)
    145 
    146     prepend(
    147       context,
    148       emit("pre", emit("code", lines, classes), attrs)
    149     )
    150   end
    151 
    152   #########
    153   # Lists #
    154   #########
    155   @start_rgx ~r{\A\d+}
    156   defp render_block(
    157          %Block.List{type: type, bullet: bullet, blocks: items, attrs: attrs},
    158          context,
    159          _loose?
    160        ) do
    161     context1 = render(items, clear_value(context))
    162 
    163     start_map =
    164       case bullet && Regex.run(@start_rgx, bullet) do
    165         nil -> %{}
    166         ["1"] -> %{}
    167         [start1] -> %{start: _normalize_start(start1)}
    168       end
    169 
    170     prepend(
    171       context,
    172       emit(to_string(type), context1.value, merge_attrs(attrs, start_map)),
    173       context1
    174     )
    175   end
    176 
    177   # format a spaced list item
    178   defp render_block(
    179          %Block.ListItem{blocks: blocks, attrs: attrs, loose?: loose?},
    180          context,
    181          _loose?
    182        ) do
    183     context1 = render(blocks, clear_value(context), loose?)
    184     prepend(
    185       context,
    186       emit("li", context1.value, attrs),
    187       context1
    188     )
    189   end
    190 
    191   ########
    192   # Text #
    193   ########
    194 
    195   defp render_block(%Block.Text{line: line, lnb: lnb}, context, loose?) do
    196     context1 = convert(line, lnb, clear_value(context))
    197     ast = context1.value |> Enum.reverse()
    198 
    199     if loose? do
    200       modify_value(context1, fn _ -> [emit("p", ast)] end)
    201     else
    202       modify_value(context1, fn _ -> ast end)
    203     end
    204   end
    205 
    206   ##################
    207   # Footnote Block #
    208   ##################
    209 
    210   @empty_set MapSet.new([])
    211   defp render_block(%Block.FnList{}=fn_list, context, _loose?) do
    212     if MapSet.equal?(context.referenced_footnote_ids, @empty_set) do
    213       context
    214     else
    215       render_defined_fns(fn_list, context)
    216     end
    217   end
    218   #######################################
    219   # Isolated IALs are rendered as paras #
    220   #######################################
    221 
    222   defp render_block(%Block.Ial{verbatim: verbatim}, context, _loose?) do
    223     prepend(context, emit("p", "{:#{verbatim}}"))
    224   end
    225 
    226   ####################
    227   # IDDef is ignored #
    228   ####################
    229 
    230   defp render_block(%Block.IdDef{}, context, _loose?), do: context
    231 
    232   # Helpers
    233   # -------
    234 
    235   # Seems to be dead code but as GFM list handling is broken maybe we have a bug
    236   # that does not call this correctly, anyhow AST triplets do not exits anymore
    237   # so this code would break if called
    238   # defp _fix_text_lines(ast, loose?)
    239   # defp _fix_text_lines(ast, false), do: Enum.map(ast, &_fix_tight_text_line/1)
    240   # defp _fix_text_lines(ast, true), do: Enum.map(ast, &_fix_loose_text_line/1)
    241 
    242   # defp _fix_loose_text_line(node)
    243   # defp _fix_loose_text_line({:text, _, lines}), do: emit("p", lines)
    244   # defp _fix_loose_text_line(node), do: node
    245 
    246   # defp _fix_tight_text_line(node)
    247   # defp _fix_tight_text_line({:text, _, lines}), do: lines
    248   # defp _fix_tight_text_line(node), do: node
    249 
    250   # INLINE CANDIDATE
    251   defp _normalize_start(start) do
    252     case String.trim_leading(start, "0") do
    253       "" -> "0"
    254       start1 -> start1
    255     end
    256   end
    257 
    258 end
    259 
    260 # SPDX-License-Identifier: Apache-2.0