lib/chaps/ignore.ex

defmodule Chaps.Ignore do
  @moduledoc """
  Handles comments to start/stop ignoring lines from coverage.
  """

  @doc """
  Filters out lines between start and end comment.
  """
  def filter(info) do
    Enum.map(info, &do_filter/1)
  end

  defp do_filter(%{name: name, source: source, coverage: coverage}) do
    lines = String.split(source, "\n")

    list =
      Enum.zip(lines, coverage)
      |> Enum.map_reduce(false, &check_and_swap/2)
      |> elem(0)
      |> List.zip()
      |> Enum.map(&Tuple.to_list(&1))

    [source, coverage] = parse_filter_list(list)
    %{name: name, source: source, coverage: coverage}
  end

  defp check_and_swap({line, coverage}, ignore) do
    {
      coverage_for_line({line, coverage}, ignore),
      ignore_next?(line, ignore)
    }
  end

  defp parse_filter_list([]), do: ["", []]

  defp parse_filter_list([lines, coverage]),
    do: [Enum.join(lines, "\n"), coverage]

  defp coverage_for_line({line, coverage}, ignore) do
    if ignore == false do
      {line, coverage}
    else
      {line, nil}
    end
  end

  defp ignore_next?(line, ignore) do
    case Regex.run(~r/chaps-ignore-(start|stop)/, line, capture: :all_but_first) do
      ["start"] -> true
      ["stop"] -> false
      _sth -> ignore
    end
  end
end