defmodule Makeup.Lexers.ElixirLexer.Helper do
  @moduledoc false
  import NimbleParsec
  alias Makeup.Lexer.Combinators

  def with_optional_separator(combinator, separator) when is_binary(separator) do
    combinator |> repeat(string(separator) |> concat(combinator))
  end

  # Allows escaping of the first character of a right delimiter.
  # This is used in sigils that don't support interpolation or character escapes but
  # must support escaping of the right delimiter.
  def escape_delim(rdelim) do
    rdelim_first_char = String.slice(rdelim, 0..0)
    string("\\" <> rdelim_first_char)
  end

  def sigil(ldelim, rdelim, ranges, middle) do
    left = string("~") |> utf8_char(ranges) |> string(ldelim)
    right = string(rdelim)

    choices = middle ++ [utf8_char([])]

    left
    |> repeat(lookahead_not(right) |> choice(choices))
    |> concat(right)
    |> optional(utf8_string([?a..?z, ?A..?Z, ?0..?9], min: 1))
    |> post_traverse({__MODULE__, :build_sigil, []})
  end

  def build_sigil(rest, acc, context, line, offset) do
    type =
      case Enum.at(acc, -2) do
        sigil when sigil in ~c"sScC" -> :string
        sigil when sigil in ~c"rR" -> :string_regex
        sigil when sigil in ~c"TDNU" -> :literal_date
        _ -> :string_sigil
      end

    Combinators.collect_raw_chars_and_binaries(rest, acc, context, line, offset, type, %{})
  end

  def escaped(literal) when is_binary(literal) do
    string("\\" <> literal)
  end

  def keyword_matcher(kind, fun_name, words) do
    heads =
      for {ttype, words} <- words do
        for word <- words do
          case kind do
            :defp ->
              quote do
                defp unquote(fun_name)([{:name, attrs, unquote(ttype)} | tokens]) do
                  [{unquote(ttype), attrs, unquote(word)} | unquote(fun_name)(tokens)]
                end
              end
              |> IO.inspect()

            :def ->
              quote do
                def unquote(fun_name)([{:name, attrs, unquote(ttype)} | tokens]) do
                  [{unquote(ttype), attrs, unquote(word)} | unquote(fun_name)(tokens)]
                end
              end
          end
        end
      end

    quote do
      (unquote_splicing(heads))
    end
  end
end
