diff --git a/lib/mix/lib/mix/tasks/format.ex b/lib/mix/lib/mix/tasks/format.ex index 2951ceac1ac..0abd1be3585 100644 --- a/lib/mix/lib/mix/tasks/format.ex +++ b/lib/mix/lib/mix/tasks/format.ex @@ -703,19 +703,25 @@ defmodule Mix.Tasks.Format do defp find_formatter_for_file(file, formatter_opts) do ext = Path.extname(file) + base_formatter = + cond do + ext in ~w(.ex .exs) -> + &elixir_format(&1, [file: file] ++ formatter_opts) + + true -> + & &1 + end + cond do plugins = find_plugins_for_extension(formatter_opts, ext) -> fn input -> - Enum.reduce(plugins, input, fn plugin, input -> + Enum.reduce(plugins, base_formatter.(input), fn plugin, input -> plugin.format(input, [extension: ext, file: file] ++ formatter_opts) end) end - ext in ~w(.ex .exs) -> - &elixir_format(&1, [file: file] ++ formatter_opts) - true -> - & &1 + base_formatter end end diff --git a/lib/mix/test/mix/tasks/format_test.exs b/lib/mix/test/mix/tasks/format_test.exs index f828e5c4790..fc6e4d5b00a 100644 --- a/lib/mix/test/mix/tasks/format_test.exs +++ b/lib/mix/test/mix/tasks/format_test.exs @@ -572,6 +572,125 @@ defmodule Mix.Tasks.FormatTest do end) end + defmodule Elixir.ExtensionExDoubleNewlinePlugin do + @behaviour Mix.Tasks.Format + + def features(opts) do + assert opts[:from_formatter_exs] == :yes + [extensions: ~w(.ex .exs), sigils: []] + end + + def format(contents, opts) do + assert opts[:from_formatter_exs] == :yes + assert opts[:extension] == ".ex" + assert opts[:file] =~ ~r/\/a\.ex$/ + contents |> String.split("\n") |> Enum.join("\n\n") + end + end + + defmodule Elixir.ExtensionExUpcasePlugin do + @behaviour Mix.Tasks.Format + + def features(opts) do + assert opts[:from_formatter_exs] == :yes + [extensions: ~w(.ex .exs), sigils: []] + end + + def format(contents, opts) do + assert opts[:from_formatter_exs] == :yes + assert opts[:extension] == ".ex" + assert opts[:file] =~ ~r/\/a\.ex$/ + String.upcase(contents) + end + end + + test "uses plugin from .formatter.exs which targets .ex files, after standard Elixir code formatting", + context do + in_tmp(context.test, fn -> + File.write!(".formatter.exs", """ + [ + inputs: ["a.ex"], + plugins: [ExtensionExUpcasePlugin], + from_formatter_exs: :yes + ] + """) + + File.write!("a.ex", """ + def sigil_test( assigns ) do + ~W"foo bar baz"abc + end + """) + + Mix.Tasks.Format.run([]) + + assert File.read!("a.ex") == """ + DEF SIGIL_TEST(ASSIGNS) DO + ~W"FOO BAR BAZ"ABC + END + """ + end) + end + + test "uses multiple plugins from .formatter.exs which target .ex files, after standard Elixir code formatting", + context do + in_tmp(context.test, fn -> + File.write!(".formatter.exs", """ + [ + inputs: ["a.ex"], + plugins: [ExtensionExDoubleNewlinePlugin, ExtensionExUpcasePlugin], + from_formatter_exs: :yes + ] + """) + + File.write!("a.ex", """ + def sigil_test(assigns ) do + ~W"foo bar baz"abc + end + """) + + Mix.Tasks.Format.run([]) + + assert File.read!("a.ex") == """ + DEF SIGIL_TEST(ASSIGNS) DO + + ~W"FOO BAR BAZ"ABC + + END + + """ + end) + end + + test "uses multiple plugins from .formatter.exs which target both .ex files and sigils within them, after standard Elixir code formatting", + context do + in_tmp(context.test, fn -> + File.write!(".formatter.exs", """ + [ + inputs: ["a.ex"], + plugins: [ExtensionExDoubleNewlinePlugin, SigilWPlugin, NewlineToDotPlugin, ExtensionExUpcasePlugin], + from_formatter_exs: :yes + ] + """) + + File.write!("a.ex", """ + def sigil_test( assigns) do + ~W"foo bar baz"abc + end + """) + + Mix.Tasks.Format.run([]) + + assert File.read!("a.ex") == """ + DEF SIGIL_TEST(ASSIGNS) DO + + ~W"FOO.BAR.BAZ"ABC + + END + + """ + end) + end + defmodule Elixir.SigilOuterPlugin do @behaviour Mix.Tasks.Format