defmodule Rewrite.DotFormatter do @moduledoc """ This module provides the information from `.formatter.exs`. """ @presistent_term_key {:rewrite, :dot_formatter} @doc """ Returns the options from the `.formatter.exs`. """ @spec opts :: keyword() def opts do with nil <- get_opts() do ".formatter.exs" |> eval_file_with_keyword_list() |> eval_deps_opts() |> tap(&put_opts/1) end end defp get_opts do :persistent_term.get(@presistent_term_key, nil) end defp put_opts(opts) do :persistent_term.put(@presistent_term_key, opts) end @doc """ Returns the option `inputs` from the `.formatter.exs`. """ @spec inputs :: list() def inputs, do: Keyword.get(opts(), :inputs, []) @doc """ Returns the option `locals_without_parens` from the `.formatter.exs`. """ @spec locals_without_parens :: keyword() def locals_without_parens, do: Keyword.get(opts(), :locals_without_parens, []) defp eval_deps_opts(formatter_opts) do eval_deps_opts(formatter_opts, Keyword.get(formatter_opts, :import_deps, [])) end defp eval_deps_opts(formatter_opts, []) do formatter_opts end defp eval_deps_opts(formatter_opts, deps) do deps_paths = Mix.Project.deps_paths() locals_without_parens = for dep <- deps, dep_path = assert_valid_dep_and_fetch_path(dep, deps_paths), dep_dot_formatter = Path.join(dep_path, ".formatter.exs"), File.regular?(dep_dot_formatter), dep_opts = eval_file_with_keyword_list(dep_dot_formatter), parenless <- dep_opts[:export][:locals_without_parens] || [], uniq: true, do: parenless Keyword.update( formatter_opts, :locals_without_parens, locals_without_parens, fn list -> list ++ locals_without_parens end ) end defp assert_valid_dep_and_fetch_path(dep, deps_paths) when is_atom(dep) do case Map.fetch(deps_paths, dep) do {:ok, path} -> if File.dir?(path) do path else Mix.raise(""" Unavailable dependency #{inspect(dep)} given to :import_deps in the formatter \ configuration. The dependency cannot be found in the file system, please run \ "mix deps.get" and try again\ """) end :error -> Mix.raise(""" Unknown dependency #{inspect(dep)} given to :import_deps in the formatter \ configuration. The dependency is not listed in your mix.exs for environment \ #{inspect(Mix.env())}\ """) end end defp assert_valid_dep_and_fetch_path(dep, _deps_paths) do Mix.raise("Dependencies in :import_deps should be atoms, got: #{inspect(dep)}") end defp eval_file_with_keyword_list(path) do {opts, _} = Code.eval_file(path) unless Keyword.keyword?(opts) do Mix.raise("Expected #{inspect(path)} to return a keyword list, got: #{inspect(opts)}") end opts end end