defmodule EarmarkParser.LineScanner do @moduledoc false alias EarmarkParser.{Helpers, Line, Options} # This is the re that matches the ridiculous "[id]: url title" syntax @id_title_part ~S""" (?| " (.*) " # in quotes | ' (.*) ' # | \( (.*) \) # in parens ) """ @id_re ~r''' ^\[([^^].*?)\]: # [someid]: \s+ (?| < (\S+) > # url in <>s | (\S+) # or without ) (?: \s+ # optional title #{@id_title_part} )? \s* $ '''x @indent_re ~r''' \A ( (?: \s{4})+ ) (\s*) # 4 or more leading spaces (.*) # the rest '''x @void_tags ~w{area br hr img wbr} @void_tag_rgx ~r''' ^<( #{Enum.join(@void_tags, "|")} ) .*? > '''x @doc false def void_tag?(tag), do: Regex.match?(@void_tag_rgx, "<#{tag}>") def scan_lines(lines, options, recursive) do _lines_with_count(lines, options.line - 1) |> _with_lookahead(options, recursive) end def type_of(line, recursive) when is_boolean(recursive), do: type_of(line, %Options{}, recursive) def type_of({line, lnb}, options = %Options{annotations: annotations}, recursive) when is_binary(line) do {line1, annotation} = line |> Helpers.expand_tabs() |> Helpers.remove_line_ending(annotations) %{_type_of(line1, options, recursive) | annotation: annotation, lnb: lnb} end def type_of({line, lnb}, _, _) do raise ArgumentError, "line number #{lnb} #{inspect line} is not a binary" end defp _type_of(line, options = %Options{}, recursive) do {ial, stripped_line} = Helpers.extract_ial(line) {content, indent} = _count_indent(line, 0) lt_four? = indent < 4 cond do content == "" -> _create_text(line, content, indent) lt_four? && !recursive && Regex.run(~r/\A \z/x, content) -> %Line.HtmlComment{complete: true, indent: indent, line: line} lt_four? && !recursive && Regex.run(~r/\A /u, options, recursive)] other -> [other | _with_lookahead(lines, options, recursive)] end end defp _with_lookahead([], _options, _recursive), do: [] defp _lookahead_until_match([], _, _, _), do: [] defp _lookahead_until_match([{line, lnb} | lines], regex, options, recursive) do if line =~ regex do [type_of({line, lnb}, options, recursive) | _with_lookahead(lines, options, recursive)] else [ %{_create_text(line) | lnb: lnb} | _lookahead_until_match(lines, regex, options, recursive) ] end end @column_rgx ~r{\A[\s|:-]+\z} defp _determine_if_header(columns) do columns |> Enum.all?(fn col -> Regex.run(@column_rgx, col) end) end defp _split_table_columns(line) do line |> String.split(~r{(? Enum.map(&String.trim/1) |> Enum.map(fn col -> Regex.replace(~r{\\\|}, col, "|") end) end end # SPDX-License-Identifier: Apache-2.0