lib/examples_styler.ex

defmodule ExamplesStyler do
  @moduledoc """
  Styles code examples in ".ex", ".exs", ".md", and ".cheatmd" files. Add to `:plugins`
  value of your `.formatter.exs` file.
  """

  @behaviour Mix.Tasks.Format

  alias ExamplesStyler.Utils
  alias Mix.Tasks.Format

  @doc false
  @impl Format
  @spec features(Utils.format_opts()) :: [sigils: [atom()], extensions: [String.t()]]
  def features(_), do: [sigils: [], extensions: [".ex", ".exs", ".md", ".cheatmd"]]

  @doc false
  @impl Format
  @spec format(String.t(), Utils.format_opts()) :: String.t()
  def format(input, opts) do
    sigils = opts |> Keyword.fetch!(:sigils) |> MapSet.new()
    extension = Keyword.fetch!(opts, :extension)

    elixir_plugin = Utils.find_elixir_formatter(opts)

    Enum.reduce([elixir_plugin, ExamplesStyler.Examples], input, fn plugin, input ->
      features = plugin.features(opts)
      plugin_sigils = features |> Keyword.fetch!(:sigils) |> MapSet.new()
      plugin_extensions = Keyword.fetch!(features, :extensions)

      if extension in plugin_extensions or not MapSet.disjoint?(sigils, plugin_sigils) do
        plugin.format(input, opts)
      else
        input
      end
    end)
  end
end