zf

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

list_parser.ex (7525B)


      1 defmodule EarmarkParser.Parser.ListParser do
      2   alias EarmarkParser.{Block, Line, Options}
      3   alias EarmarkParser.Parser.ListInfo
      4 
      5   import EarmarkParser.Helpers.StringHelpers, only: [behead: 2]
      6   import EarmarkParser.Message, only: [add_message: 2]
      7   import ListInfo
      8 
      9   @moduledoc false
     10 
     11   @not_pending {nil, 0}
     12 
     13   def parse_list(lines, result, options \\ %Options{}) do
     14     {items, rest, options1} = _parse_list_items_init(lines, [], options)
     15     list                    = _make_list(items, _empty_list(items) )
     16     {[list|result], rest, options1}
     17   end
     18 
     19   defp _parse_list_items_init([item|rest], list_items, options) do
     20     options1 = %{options|line: item.lnb}
     21     _parse_list_items_start(rest, _make_and_prepend_list_item(item, list_items), new(item, options1))
     22   end
     23 
     24   defp _parse_list_items_spaced(input, items, list_info)
     25   defp _parse_list_items_spaced(input, items, %{pending: @not_pending}=list_info) do
     26     _parse_list_items_spaced_np(input, items, list_info)
     27   end
     28   defp _parse_list_items_spaced(input, items, list_info) do
     29     _parse_list_items_spaced_pdg(input, items, list_info)
     30   end
     31 
     32   defp _parse_list_items_spaced_np([%Line.Blank{}|rest], items, list_info) do
     33     list_info1 = %{list_info|lines: [""|list_info.lines], options: %{list_info.options|line: list_info.options.line + 1}}
     34     _parse_list_items_spaced_np(rest, items, list_info1)
     35   end
     36   defp _parse_list_items_spaced_np([%Line.Ruler{}|_]=lines, items, list_info) do
     37     _finish_list_items(lines, items, false, list_info)
     38   end
     39   defp _parse_list_items_spaced_np([%Line.ListItem{indent: ii}=item|_]=input, list_items, %{width: w}=list_info)
     40     when ii < w do
     41       if _starts_list?(item, list_items) do
     42         _finish_list_items(input, list_items, false, list_info)
     43       else
     44         {items1, options1} = _finish_list_item(list_items, false, _loose(list_info))
     45         _parse_list_items_init(input, items1, options1)
     46       end
     47   end
     48   defp _parse_list_items_spaced_np([%Line.Indent{indent: ii}=item|rest], list_items, %{width: w}=list_info)
     49     when ii >= w do
     50       indented = _behead_spaces(item.line, w)
     51       _parse_list_items_spaced(rest, list_items, update_list_info(list_info, indented, item, true))
     52   end
     53   defp _parse_list_items_spaced_np([%Line.ListItem{}=line|rest], items, list_info) do
     54     indented = _behead_spaces(line.line, list_info.width)
     55     _parse_list_items_start(rest, items, update_list_info(list_info, indented, line))
     56   end
     57   # BUG: Still do not know how much to indent here???
     58   defp _parse_list_items_spaced_np([%{indent: indent, line: str_line}=line|rest], items, %{width: width}=list_info) when
     59     indent >= width
     60   do
     61     _parse_list_items_spaced(rest, items, update_list_info(list_info, behead(str_line, width), line, true))
     62   end
     63   defp _parse_list_items_spaced_np(input, items, list_info) do
     64     _finish_list_items(input ,items, false, list_info)
     65   end
     66 
     67   defp _parse_list_items_spaced_pdg(input, items, list_info)
     68   defp _parse_list_items_spaced_pdg([], items, %{pending: {pending, lnb}}=list_info) do
     69     options1 =
     70       add_message(list_info.options, {:warning, lnb, "Closing unclosed backquotes #{pending} at end of input"})
     71     _finish_list_items([], items, false, %{list_info| options: options1})
     72   end
     73   defp _parse_list_items_spaced_pdg([line|rest], items, list_info) do
     74     indented = _behead_spaces(line.line, list_info.width)
     75     _parse_list_items_spaced(rest, items, update_list_info(list_info, indented, line, true))
     76   end
     77 
     78 
     79   defp _parse_list_items_start(input, list_items, list_info)
     80   defp _parse_list_items_start(input, list_items, %{pending: @not_pending}=list_info) do
     81     _parse_list_items_start_np(input, list_items, list_info)
     82   end
     83   defp _parse_list_items_start(input, list_items, list_info) do
     84     _parse_list_items_start_pdg(input, list_items, list_info)
     85   end
     86 
     87   defp _parse_list_items_start_np(input, list_items, list_info)
     88   defp _parse_list_items_start_np([%Line.Blank{}|input], items, list_info) do
     89     _parse_list_items_spaced(input, items, prepend_line(list_info, ""))
     90   end
     91   defp _parse_list_items_start_np([], list_items, list_info) do
     92     _finish_list_items([], list_items, true, list_info)
     93   end
     94   defp _parse_list_items_start_np([%Line.Ruler{}|_]=input, list_items, list_info) do
     95     _finish_list_items(input, list_items, true, list_info)
     96   end
     97   defp _parse_list_items_start_np([%Line.Heading{}|_]=input, list_items, list_info) do
     98     _finish_list_items(input, list_items, true, list_info)
     99   end
    100   defp _parse_list_items_start_np([%Line.ListItem{indent: ii}=item|_]=input, list_items, %{width: w}=list_info)
    101     when ii < w do
    102       if _starts_list?(item, list_items) do
    103         _finish_list_items(input, list_items, true, list_info)
    104       else
    105         {items1, options1} = _finish_list_item(list_items, true, list_info)
    106         _parse_list_items_init(input, items1, options1)
    107       end
    108   end
    109   # Slurp in everything else before a first blank line
    110   defp _parse_list_items_start_np([%{line: str_line}=line|rest], items, list_info) do
    111     indented = _behead_spaces(str_line, list_info.width)
    112     _parse_list_items_start(rest, items, update_list_info(list_info, indented, line))
    113   end
    114 
    115   defp _parse_list_items_start_pdg(input, items, list_info)
    116   defp _parse_list_items_start_pdg([], items, list_info) do
    117     _finish_list_items([], items, true, list_info)
    118   end
    119   defp _parse_list_items_start_pdg([line|rest], items, list_info) do
    120     _parse_list_items_start(rest, items, update_list_info(list_info, line.line, line))
    121   end
    122 
    123   defp _behead_spaces(str, len) do
    124     Regex.replace(~r/\A\s{1,#{len}}/, str, "")
    125   end
    126 
    127   # INLINE CANDIDATE
    128   defp _empty_list([%Block.ListItem{loose?: loose?, type: type}|_]) do
    129     %Block.List{loose?: loose?, type: type}
    130   end
    131 
    132   @start_number_rgx ~r{\A0*(\d+)\.}
    133   defp _extract_start(%{bullet: bullet}) do
    134     case Regex.run(@start_number_rgx, bullet) do
    135       nil -> ""
    136       [_, "1"] -> ""
    137       [_, start] -> ~s{ start="#{start}"}
    138     end
    139   end
    140 
    141   defp _finish_list_item([%Block.ListItem{}=item|items], _at_start?, list_info) do
    142     {blocks, _, _, options1} = list_info.lines
    143                             |> Enum.reverse
    144                             |> EarmarkParser.Parser.parse(%{list_info.options|line: item.lnb}, :list)
    145     loose1? = _already_loose?(items) || list_info.loose?
    146     {[%{item | blocks: blocks, loose?: loose1?}|items], options1}
    147   end
    148 
    149   defp _finish_list_items(input, items, at_start?, list_info) do
    150     {items1, options1} = _finish_list_item(items, at_start?, list_info)
    151     {items1, input, options1}
    152   end
    153 
    154   defp _make_and_prepend_list_item(%Line.ListItem{bullet: bullet, lnb: lnb, type: type}, list_items) do
    155     [%Block.ListItem{bullet: bullet, lnb: lnb, spaced?: false, type: type}|list_items]
    156   end
    157 
    158   defp _make_list(items, list)
    159   defp _make_list([%Block.ListItem{bullet: bullet, lnb: lnb}=item], %Block.List{loose?: loose?}=list) do
    160     %{list | blocks: [%{item | loose?: loose?}|list.blocks],
    161       bullet: bullet,
    162       lnb: lnb,
    163       start: _extract_start(item)}
    164   end
    165   defp _make_list([%Block.ListItem{}=item|rest], %Block.List{loose?: loose?}=list) do
    166    _make_list(rest, %{list | blocks: [%{item | loose?: loose?}|list.blocks]})
    167   end
    168 
    169   defp _already_loose?(items)
    170   defp _already_loose?([]), do: false
    171   defp _already_loose?([%{loose?: loose?}|_]), do: loose?
    172 
    173   defp _loose(list_info), do: %{list_info|loose?: true}
    174 
    175   defp _starts_list?(%{bullet: bullet1}, [%Block.ListItem{bullet: bullet2}|_]) do
    176     String.last(bullet1) != String.last(bullet2)
    177   end
    178 
    179 end
    180 # SPDX-License-Identifier: Apache-2.0