lib/logical_file/macros/line_comment.ex

defmodule LogicalFile.Macros.LineComment do
  @behaviour LogicalFile.Macro

  alias LogicalFile.Section

  @moduledoc """
  A sample implementation of a macro that supports single-line comments where
  a comment expression is recognised at the beginning of a line and it
  transforms the entire content of that line into whitespace. Note that this
  does not allow a line comment to appear at the end of an expression!

  While a regular expression could be specified to recognise whitespace as part
  of the comment marker a more sophisticated implementation would allow the
  comment marker to appear after an expression. It is also left as an exercise
  to implement multi-line comments (a la C /*...*/)
  """

  @impl LogicalFile.Macro
  def apply_macro(%LogicalFile{} = file, options \\ []) do
    case Keyword.get(options, :expr) do
      nil -> raise "Cannot process comment macros without expression (:expr)!"
      expr -> process_comments(file, expr)
    end
  end

  @impl LogicalFile.Macro
  def invocation(options) when is_list(options) do
    case Keyword.get(options, :expr) do
      nil ->
        raise "Must specify expr: as Regex to match single line comment!"

      expr when is_struct(expr, Regex) ->
        {__MODULE__, [expr: expr]}

      _ ->
        raise "Illegal expr: must be Regex"
    end
  end

  @doc """
  The general strategy is to process sections in order.
  For each section find any line matching the expression and
  transform the entire contents of the line into whitespace.
  """
  def process_comments(%LogicalFile{} = file, %Regex{} = expr) do
    processed_sections =
      file
      |> LogicalFile.sections_in_order()
      |> Enum.map(fn section ->
        section
        |> Section.lines_matching(expr)
        |> Enum.reduce(section, fn {lno, _line}, updated_section ->
          Section.update_line(updated_section, lno, fn line -> Regex.replace(~r/./, line, " ") end)
        end)
      end)

    %{file | sections: LogicalFile.sections_to_map(processed_sections)}
  end
end