diff --git a/lib/elixir/lib/code/formatter.ex b/lib/elixir/lib/code/formatter.ex index 635d41f8968..7c0dd8bda31 100644 --- a/lib/elixir/lib/code/formatter.ex +++ b/lib/elixir/lib/code/formatter.ex @@ -181,7 +181,7 @@ defmodule Code.Formatter do sigils = Map.new(sigils, fn {key, value} -> - with true <- is_atom(key) and is_function(value, 1), + with true <- is_atom(key) and is_function(value, 2), [char] <- Atom.to_charlist(key), true <- char in ?A..?Z do {char, value} @@ -189,7 +189,7 @@ defmodule Code.Formatter do _ -> raise ArgumentError, ":sigils must be a keyword list with a single uppercased letter as key and an " <> - "anonymous function expecting a single argument as value, got: #{inspect(sigils)}" + "anonymous function expecting two arguments as value, got: #{inspect(sigils)}" end end) @@ -1320,7 +1320,7 @@ defmodule Code.Formatter do entries = case state.sigils do %{^name => callback} -> - case callback.(hd(entries)) do + case callback.(hd(entries), sigil: List.to_atom([name]), modifiers: modifiers) do binary when is_binary(binary) -> [binary] diff --git a/lib/elixir/test/elixir/code_formatter/general_test.exs b/lib/elixir/test/elixir/code_formatter/general_test.exs index 9559087fbac..5aed5d17cec 100644 --- a/lib/elixir/test/elixir/code_formatter/general_test.exs +++ b/lib/elixir/test/elixir/code_formatter/general_test.exs @@ -125,7 +125,11 @@ defmodule Code.Formatter.GeneralTest do ~W/foo bar baz/ """ - formatter = &(&1 |> String.split(~r/ +/) |> Enum.join(" ")) + formatter = fn content, opts -> + assert opts == [sigil: :W, modifiers: []] + content |> String.split(~r/ +/) |> Enum.join(" ") + end + assert_format bad, good, sigils: [W: formatter] bad = """ @@ -136,7 +140,11 @@ defmodule Code.Formatter.GeneralTest do var = ~W/foo bar baz/abc """ - formatter = &(&1 |> String.split(~r/ +/) |> Enum.join(" ")) + formatter = fn content, opts -> + assert opts == [sigil: :W, modifiers: 'abc'] + content |> String.split(~r/ +/) |> Enum.join(" ") + end + assert_format bad, good, sigils: [W: formatter] end @@ -153,7 +161,11 @@ defmodule Code.Formatter.GeneralTest do ''' """ - formatter = &(&1 |> String.split(~r/ +/) |> Enum.join(" ")) + formatter = fn content, opts -> + assert opts == [sigil: :W, modifiers: []] + content |> String.split(~r/ +/) |> Enum.join(" ") + end + assert_format bad, good, sigils: [W: formatter] bad = """ @@ -176,7 +188,11 @@ defmodule Code.Formatter.GeneralTest do end """ - formatter = &(&1 |> String.split(~r/ +/) |> Enum.join("\n")) + formatter = fn content, opts -> + assert opts == [sigil: :W, modifiers: 'abc'] + content |> String.split(~r/ +/) |> Enum.join("\n") + end + assert_format bad, good, sigils: [W: formatter] end end diff --git a/lib/mix/lib/mix/tasks/format.ex b/lib/mix/lib/mix/tasks/format.ex index 1fd5722ccae..b9e7f6c9a8b 100644 --- a/lib/mix/lib/mix/tasks/format.ex +++ b/lib/mix/lib/mix/tasks/format.ex @@ -102,6 +102,14 @@ defmodule Mix.Tasks.Format do end end + The `opts` passed to `format/2` contains all the formatting options and either: + + * `:sigil` (atom) - the sigil being formatted, e.g. `:M`. + + * `:modifiers` (charlist) - list of sigil modifiers. + + * `:extension` (string) - the extension of the file being formatted, e.g. `".md"`. + Now any application can use your formatter as follows: # .formatters.exs @@ -490,7 +498,7 @@ defmodule Mix.Tasks.Format do &elixir_format(&1, [file: file] ++ formatter_opts) plugin = find_plugin_for_extension(formatter_opts, ext) -> - &plugin.format(&1, formatter_opts) + &plugin.format(&1, [extension: ext] ++ formatter_opts) true -> & &1 @@ -531,7 +539,7 @@ defmodule Mix.Tasks.Format do sigils = for plugin <- Keyword.fetch!(formatter_opts, :plugins), sigil <- find_sigils_from_plugins(plugin, formatter_opts), - do: {sigil, &plugin.format(&1, formatter_opts)} + do: {sigil, &plugin.format(&1, &2 ++ formatter_opts)} IO.iodata_to_binary([Code.format_string!(content, [sigils: sigils] ++ formatter_opts), ?\n]) end diff --git a/lib/mix/test/mix/tasks/format_test.exs b/lib/mix/test/mix/tasks/format_test.exs index aacb19f128f..6473cfd4a71 100644 --- a/lib/mix/test/mix/tasks/format_test.exs +++ b/lib/mix/test/mix/tasks/format_test.exs @@ -199,11 +199,13 @@ defmodule Mix.Tasks.FormatTest do def features(opts) do assert opts[:from_formatter_exs] == :yes - [sigils: [:W], extensions: ~w(.w)] + [sigils: [:W]] end def format(contents, opts) do assert opts[:from_formatter_exs] == :yes + assert opts[:sigil] == :W + assert opts[:modifiers] == 'abc' contents |> String.split(~r/\s/) |> Enum.join("\n") end end @@ -240,12 +242,27 @@ defmodule Mix.Tasks.FormatTest do end) end + defmodule Elixir.ExtensionWPlugin do + @behaviour Mix.Tasks.Format + + def features(opts) do + assert opts[:from_formatter_exs] == :yes + [extensions: ~w(.w)] + end + + def format(contents, opts) do + assert opts[:from_formatter_exs] == :yes + assert opts[:extension] == ".w" + contents |> String.split(~r/\s/) |> Enum.join("\n") + end + end + test "uses extension plugins from .formatter.exs", context do in_tmp(context.test, fn -> File.write!(".formatter.exs", """ [ inputs: ["a.w"], - plugins: [SigilWPlugin], + plugins: [ExtensionWPlugin], from_formatter_exs: :yes ] """)