summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@dashbit.co>2023-05-07 21:34:50 +0200
committerJosé Valim <jose.valim@dashbit.co>2023-05-07 21:34:50 +0200
commita395937656853fa8ff18826ca13ab9658bb297b2 (patch)
tree826592ea14932e2d8bc848ffb572d15b2b3f4093
parent78fe565bba599f3dada5f3d7ba947ed514e4d730 (diff)
downloadelixir-a395937656853fa8ff18826ca13ab9658bb297b2.tar.gz
Push cwd handling all the way up in mix format
-rw-r--r--lib/mix/lib/mix/tasks/format.ex90
-rw-r--r--lib/mix/test/mix/tasks/format_test.exs17
2 files changed, 45 insertions, 62 deletions
diff --git a/lib/mix/lib/mix/tasks/format.ex b/lib/mix/lib/mix/tasks/format.ex
index a164f3aac..ff0ab84d4 100644
--- a/lib/mix/lib/mix/tasks/format.ex
+++ b/lib/mix/lib/mix/tasks/format.ex
@@ -218,8 +218,9 @@ defmodule Mix.Tasks.Format do
@impl true
def run(args) do
+ cwd = File.cwd!()
{opts, args} = OptionParser.parse!(args, strict: @switches)
- {dot_formatter, formatter_opts} = eval_dot_formatter(opts)
+ {dot_formatter, formatter_opts} = eval_dot_formatter(cwd, opts)
if opts[:check_equivalent] do
IO.warn("--check-equivalent has been deprecated and has no effect")
@@ -230,10 +231,10 @@ defmodule Mix.Tasks.Format do
end
{formatter_opts_and_subs, _sources} =
- eval_deps_and_subdirectories(dot_formatter, [], formatter_opts, [dot_formatter])
+ eval_deps_and_subdirectories(cwd, dot_formatter, formatter_opts, [dot_formatter])
args
- |> expand_args(dot_formatter, formatter_opts_and_subs, opts)
+ |> expand_args(cwd, dot_formatter, formatter_opts_and_subs, opts)
|> Task.async_stream(&format_file(&1, opts), ordered: false, timeout: :infinity)
|> Enum.reduce({[], []}, &collect_status/2)
|> check!(opts)
@@ -249,14 +250,13 @@ defmodule Mix.Tasks.Format do
"""
@doc since: "1.13.0"
def formatter_for_file(file, opts \\ []) do
- {dot_formatter, formatter_opts} = eval_dot_formatter(opts)
-
- prefix = Keyword.get(opts, :root, [])
+ cwd = Keyword.get_lazy(opts, :root, &File.cwd!/0)
+ {dot_formatter, formatter_opts} = eval_dot_formatter(cwd, opts)
{formatter_opts_and_subs, _sources} =
- eval_deps_and_subdirectories(dot_formatter, prefix, formatter_opts, [dot_formatter])
+ eval_deps_and_subdirectories(cwd, dot_formatter, formatter_opts, [dot_formatter])
- find_formatter_and_opts_for_file(file, prefix, formatter_opts_and_subs)
+ find_formatter_and_opts_for_file(file, cwd, formatter_opts_and_subs)
end
@doc """
@@ -269,17 +269,14 @@ defmodule Mix.Tasks.Format do
formatter_opts
end
- defp eval_dot_formatter(opts) do
+ defp eval_dot_formatter(cwd, opts) do
cond do
dot_formatter = opts[:dot_formatter] ->
{dot_formatter, eval_file_with_keyword_list(dot_formatter)}
- File.regular?(".formatter.exs") ->
- {".formatter.exs", eval_file_with_keyword_list(".formatter.exs")}
-
- opts[:root] && File.regular?(Path.join(opts[:root], ".formatter.exs")) ->
- dot_formatter = Path.join(opts[:root], ".formatter.exs")
- {dot_formatter, eval_file_with_keyword_list(dot_formatter)}
+ File.regular?(Path.join(cwd, ".formatter.exs")) ->
+ dot_formatter = Path.join(cwd, ".formatter.exs")
+ {".formatter.exs", eval_file_with_keyword_list(dot_formatter)}
true ->
{".formatter.exs", []}
@@ -289,7 +286,7 @@ defmodule Mix.Tasks.Format do
# This function reads exported configuration from the imported
# dependencies and subdirectories and deals with caching the result
# of reading such configuration in a manifest file.
- defp eval_deps_and_subdirectories(dot_formatter, prefix, formatter_opts, sources) do
+ defp eval_deps_and_subdirectories(cwd, dot_formatter, formatter_opts, sources) do
deps = Keyword.get(formatter_opts, :import_deps, [])
subs = Keyword.get(formatter_opts, :subdirectories, [])
plugins = Keyword.get(formatter_opts, :plugins, [])
@@ -356,7 +353,7 @@ defmodule Mix.Tasks.Format do
{{locals_without_parens, subdirectories}, sources} =
maybe_cache_in_manifest(dot_formatter, manifest, fn ->
- {subdirectories, sources} = eval_subs_opts(subs, prefix, sources)
+ {subdirectories, sources} = eval_subs_opts(subs, cwd, sources)
{{eval_deps_opts(deps), subdirectories}, sources}
end)
@@ -420,11 +417,11 @@ defmodule Mix.Tasks.Format do
do: parenless_call
end
- defp eval_subs_opts(subs, prefix, sources) do
+ defp eval_subs_opts(subs, cwd, sources) do
{subs, sources} =
Enum.flat_map_reduce(subs, sources, fn sub, sources ->
- prefix = Path.join(prefix ++ [sub])
- {Path.wildcard(prefix), [Path.join(prefix, ".formatter.exs") | sources]}
+ cwd = Path.expand(sub, cwd)
+ {Path.wildcard(cwd), [Path.join(cwd, ".formatter.exs") | sources]}
end)
Enum.flat_map_reduce(subs, sources, fn sub, sources ->
@@ -434,7 +431,7 @@ defmodule Mix.Tasks.Format do
formatter_opts = eval_file_with_keyword_list(sub_formatter)
{formatter_opts_and_subs, sources} =
- eval_deps_and_subdirectories(:in_memory, [sub], formatter_opts, sources)
+ eval_deps_and_subdirectories(sub, :in_memory, formatter_opts, sources)
{[{sub, formatter_opts_and_subs}], sources}
else
@@ -470,7 +467,7 @@ defmodule Mix.Tasks.Format do
opts
end
- defp expand_args([], dot_formatter, formatter_opts_and_subs, _opts) do
+ defp expand_args([], cwd, dot_formatter, formatter_opts_and_subs, _opts) do
if no_entries_in_formatter_opts?(formatter_opts_and_subs) do
Mix.raise(
"Expected one or more files/patterns to be given to mix format " <>
@@ -479,13 +476,13 @@ defmodule Mix.Tasks.Format do
end
dot_formatter
- |> expand_dot_inputs([], formatter_opts_and_subs, %{})
+ |> expand_dot_inputs(cwd, formatter_opts_and_subs, %{})
|> Enum.map(fn {file, {_dot_formatter, formatter_opts}} ->
{file, find_formatter_for_file(file, formatter_opts)}
end)
end
- defp expand_args(files_and_patterns, _dot_formatter, {formatter_opts, subs}, opts) do
+ defp expand_args(files_and_patterns, cwd, _dot_formatter, {formatter_opts, subs}, opts) do
files =
for file_or_pattern <- files_and_patterns,
file <- stdin_or_wildcard(file_or_pattern),
@@ -504,35 +501,34 @@ defmodule Mix.Tasks.Format do
stdin_filename = Keyword.get(opts, :stdin_filename, "stdin.exs")
{formatter, _opts} =
- find_formatter_and_opts_for_file(stdin_filename, {formatter_opts, subs})
+ find_formatter_and_opts_for_file(stdin_filename, cwd, {formatter_opts, subs})
{file, formatter}
else
- {formatter, _opts} = find_formatter_and_opts_for_file(file, {formatter_opts, subs})
+ {formatter, _opts} = find_formatter_and_opts_for_file(file, cwd, {formatter_opts, subs})
{file, formatter}
end
end
end
- defp expand_dot_inputs(dot_formatter, prefix, {formatter_opts, subs}, acc) do
+ defp expand_dot_inputs(dot_formatter, cwd, {formatter_opts, subs}, acc) do
if no_entries_in_formatter_opts?({formatter_opts, subs}) do
Mix.raise("Expected :inputs or :subdirectories key in #{dot_formatter}")
end
map =
for input <- List.wrap(formatter_opts[:inputs]),
- file <- Path.wildcard(Path.join(prefix ++ [input]), match_dot: true),
- do: {expand_relative_to_cwd(file), {dot_formatter, formatter_opts}},
+ file <- Path.wildcard(Path.expand(input, cwd), match_dot: true),
+ do: {file, {dot_formatter, formatter_opts}},
into: %{}
acc =
Map.merge(acc, map, fn file, {dot_formatter1, _}, {dot_formatter2, formatter_opts} ->
Mix.shell().error(
- "Both #{dot_formatter1} and #{dot_formatter2} specify the file " <>
- "#{Path.relative_to_cwd(file)} in their :inputs option. To resolve the " <>
- "conflict, the configuration in #{dot_formatter1} will be ignored. " <>
- "Please change the list of :inputs in one of the formatter files so only " <>
- "one of them matches #{Path.relative_to_cwd(file)}"
+ "Both #{dot_formatter1} and #{dot_formatter2} specify the file #{file} in their " <>
+ ":inputs option. To resolve the conflict, the configuration in #{dot_formatter1} " <>
+ "will be ignored. Please change the list of :inputs in one of the formatter files " <>
+ "so only one of them matches #{file}"
)
{dot_formatter2, formatter_opts}
@@ -540,17 +536,10 @@ defmodule Mix.Tasks.Format do
Enum.reduce(subs, acc, fn {sub, formatter_opts_and_subs}, acc ->
sub_formatter = Path.join(sub, ".formatter.exs")
- expand_dot_inputs(sub_formatter, [sub], formatter_opts_and_subs, acc)
+ expand_dot_inputs(sub_formatter, sub, formatter_opts_and_subs, acc)
end)
end
- defp expand_relative_to_cwd(path) do
- case File.cwd() do
- {:ok, cwd} -> Path.expand(path, cwd)
- _ -> path
- end
- end
-
defp find_formatter_for_file(file, formatter_opts) do
ext = Path.extname(file)
@@ -582,22 +571,15 @@ defmodule Mix.Tasks.Format do
if plugins != [], do: plugins, else: nil
end
- defp find_formatter_and_opts_for_file(file, formatter_opts_and_subs) do
- split = file |> Path.relative_to_cwd() |> Path.split()
- formatter_opts = recur_formatter_opts_for_file(split, formatter_opts_and_subs)
- {find_formatter_for_file(file, formatter_opts), formatter_opts}
- end
-
- defp find_formatter_and_opts_for_file(file, prefix, formatter_opts_and_subs) do
- split = file |> Path.relative_to(prefix) |> Path.split()
- formatter_opts = recur_formatter_opts_for_file(split, formatter_opts_and_subs)
+ defp find_formatter_and_opts_for_file(file, cwd, formatter_opts_and_subs) do
+ formatter_opts = recur_formatter_opts_for_file(cwd, formatter_opts_and_subs)
{find_formatter_for_file(file, formatter_opts), formatter_opts}
end
- defp recur_formatter_opts_for_file(split, {formatter_opts, subs}) do
+ defp recur_formatter_opts_for_file(cwd, {formatter_opts, subs}) do
Enum.find_value(subs, formatter_opts, fn {sub, formatter_opts_and_subs} ->
- if List.starts_with?(split, Path.split(sub)) do
- recur_formatter_opts_for_file(split, formatter_opts_and_subs)
+ if String.starts_with?(sub, cwd) do
+ recur_formatter_opts_for_file(sub, formatter_opts_and_subs)
end
end)
end
diff --git a/lib/mix/test/mix/tasks/format_test.exs b/lib/mix/test/mix/tasks/format_test.exs
index 05be1212e..d6b6793e9 100644
--- a/lib/mix/test/mix/tasks/format_test.exs
+++ b/lib/mix/test/mix/tasks/format_test.exs
@@ -744,7 +744,7 @@ defmodule Mix.Tasks.FormatTest do
[]
""")
- message = "Expected :inputs or :subdirectories key in lib/.formatter.exs"
+ message = "Expected :inputs or :subdirectories key in #{Path.expand("lib/.formatter.exs")}"
assert_raise Mix.Error, message, fn -> Mix.Tasks.Format.run([]) end
end)
end
@@ -799,16 +799,17 @@ defmodule Mix.Tasks.FormatTest do
Mix.Tasks.Format.run([])
message1 =
- "Both .formatter.exs and lib/.formatter.exs specify the file lib/a.ex in their " <>
- ":inputs option. To resolve the conflict, the configuration in .formatter.exs " <>
- "will be ignored. Please change the list of :inputs in one of the formatter files " <>
- "so only one of them matches lib/a.ex"
+ "Both .formatter.exs and #{Path.expand("lib/.formatter.exs")} specify the file " <>
+ "#{Path.expand("lib/a.ex")} in their :inputs option. To resolve the conflict, " <>
+ "the configuration in .formatter.exs will be ignored. Please change the list of " <>
+ ":inputs in one of the formatter files so only one of them matches #{Path.expand("lib/a.ex")}"
message2 =
- "Both lib/.formatter.exs and foo/.formatter.exs specify the file lib/a.ex in their " <>
- ":inputs option. To resolve the conflict, the configuration in lib/.formatter.exs " <>
+ "Both #{Path.expand("lib/.formatter.exs")} and #{Path.expand("foo/.formatter.exs")} " <>
+ "specify the file #{Path.expand("lib/a.ex")} in their :inputs option. To resolve " <>
+ "the conflict, the configuration in #{Path.expand("lib/.formatter.exs")} " <>
"will be ignored. Please change the list of :inputs in one of the formatter files " <>
- "so only one of them matches lib/a.ex"
+ "so only one of them matches #{Path.expand("lib/a.ex")}"
assert_received {:mix_shell, :error, [^message1]}
assert_received {:mix_shell, :error, [^message2]}