Skip to content

Commit b166e8e

Browse files
Pass sigil/modifiers through to formatter (#11348)
This allows the formatter plugins to format content based on whether it's a file or a sigil being formatted. The modifiers could be used for additional options.
1 parent e5e5170 commit b166e8e

File tree

4 files changed

+52
-11
lines changed

4 files changed

+52
-11
lines changed

lib/elixir/lib/code/formatter.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,15 @@ defmodule Code.Formatter do
181181

182182
sigils =
183183
Map.new(sigils, fn {key, value} ->
184-
with true <- is_atom(key) and is_function(value, 1),
184+
with true <- is_atom(key) and is_function(value, 2),
185185
[char] <- Atom.to_charlist(key),
186186
true <- char in ?A..?Z do
187187
{char, value}
188188
else
189189
_ ->
190190
raise ArgumentError,
191191
":sigils must be a keyword list with a single uppercased letter as key and an " <>
192-
"anonymous function expecting a single argument as value, got: #{inspect(sigils)}"
192+
"anonymous function expecting two arguments as value, got: #{inspect(sigils)}"
193193
end
194194
end)
195195

@@ -1320,7 +1320,7 @@ defmodule Code.Formatter do
13201320
entries =
13211321
case state.sigils do
13221322
%{^name => callback} ->
1323-
case callback.(hd(entries)) do
1323+
case callback.(hd(entries), sigil: List.to_atom([name]), modifiers: modifiers) do
13241324
binary when is_binary(binary) ->
13251325
[binary]
13261326

lib/elixir/test/elixir/code_formatter/general_test.exs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,11 @@ defmodule Code.Formatter.GeneralTest do
125125
~W/foo bar baz/
126126
"""
127127

128-
formatter = &(&1 |> String.split(~r/ +/) |> Enum.join(" "))
128+
formatter = fn content, opts ->
129+
assert opts == [sigil: :W, modifiers: []]
130+
content |> String.split(~r/ +/) |> Enum.join(" ")
131+
end
132+
129133
assert_format bad, good, sigils: [W: formatter]
130134

131135
bad = """
@@ -136,7 +140,11 @@ defmodule Code.Formatter.GeneralTest do
136140
var = ~W/foo bar baz/abc
137141
"""
138142

139-
formatter = &(&1 |> String.split(~r/ +/) |> Enum.join(" "))
143+
formatter = fn content, opts ->
144+
assert opts == [sigil: :W, modifiers: 'abc']
145+
content |> String.split(~r/ +/) |> Enum.join(" ")
146+
end
147+
140148
assert_format bad, good, sigils: [W: formatter]
141149
end
142150

@@ -153,7 +161,11 @@ defmodule Code.Formatter.GeneralTest do
153161
'''
154162
"""
155163

156-
formatter = &(&1 |> String.split(~r/ +/) |> Enum.join(" "))
164+
formatter = fn content, opts ->
165+
assert opts == [sigil: :W, modifiers: []]
166+
content |> String.split(~r/ +/) |> Enum.join(" ")
167+
end
168+
157169
assert_format bad, good, sigils: [W: formatter]
158170

159171
bad = """
@@ -176,7 +188,11 @@ defmodule Code.Formatter.GeneralTest do
176188
end
177189
"""
178190

179-
formatter = &(&1 |> String.split(~r/ +/) |> Enum.join("\n"))
191+
formatter = fn content, opts ->
192+
assert opts == [sigil: :W, modifiers: 'abc']
193+
content |> String.split(~r/ +/) |> Enum.join("\n")
194+
end
195+
180196
assert_format bad, good, sigils: [W: formatter]
181197
end
182198
end

lib/mix/lib/mix/tasks/format.ex

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ defmodule Mix.Tasks.Format do
102102
end
103103
end
104104
105+
The `opts` passed to `format/2` contains all the formatting options and either:
106+
107+
* `:sigil` (atom) - the sigil being formatted, e.g. `:M`.
108+
109+
* `:modifiers` (charlist) - list of sigil modifiers.
110+
111+
* `:extension` (string) - the extension of the file being formatted, e.g. `".md"`.
112+
105113
Now any application can use your formatter as follows:
106114
107115
# .formatters.exs
@@ -490,7 +498,7 @@ defmodule Mix.Tasks.Format do
490498
&elixir_format(&1, [file: file] ++ formatter_opts)
491499

492500
plugin = find_plugin_for_extension(formatter_opts, ext) ->
493-
&plugin.format(&1, formatter_opts)
501+
&plugin.format(&1, [extension: ext] ++ formatter_opts)
494502

495503
true ->
496504
& &1
@@ -531,7 +539,7 @@ defmodule Mix.Tasks.Format do
531539
sigils =
532540
for plugin <- Keyword.fetch!(formatter_opts, :plugins),
533541
sigil <- find_sigils_from_plugins(plugin, formatter_opts),
534-
do: {sigil, &plugin.format(&1, formatter_opts)}
542+
do: {sigil, &plugin.format(&1, &2 ++ formatter_opts)}
535543

536544
IO.iodata_to_binary([Code.format_string!(content, [sigils: sigils] ++ formatter_opts), ?\n])
537545
end

lib/mix/test/mix/tasks/format_test.exs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,13 @@ defmodule Mix.Tasks.FormatTest do
199199

200200
def features(opts) do
201201
assert opts[:from_formatter_exs] == :yes
202-
[sigils: [:W], extensions: ~w(.w)]
202+
[sigils: [:W]]
203203
end
204204

205205
def format(contents, opts) do
206206
assert opts[:from_formatter_exs] == :yes
207+
assert opts[:sigil] == :W
208+
assert opts[:modifiers] == 'abc'
207209
contents |> String.split(~r/\s/) |> Enum.join("\n")
208210
end
209211
end
@@ -240,12 +242,27 @@ defmodule Mix.Tasks.FormatTest do
240242
end)
241243
end
242244

245+
defmodule Elixir.ExtensionWPlugin do
246+
@behaviour Mix.Tasks.Format
247+
248+
def features(opts) do
249+
assert opts[:from_formatter_exs] == :yes
250+
[extensions: ~w(.w)]
251+
end
252+
253+
def format(contents, opts) do
254+
assert opts[:from_formatter_exs] == :yes
255+
assert opts[:extension] == ".w"
256+
contents |> String.split(~r/\s/) |> Enum.join("\n")
257+
end
258+
end
259+
243260
test "uses extension plugins from .formatter.exs", context do
244261
in_tmp(context.test, fn ->
245262
File.write!(".formatter.exs", """
246263
[
247264
inputs: ["a.w"],
248-
plugins: [SigilWPlugin],
265+
plugins: [ExtensionWPlugin],
249266
from_formatter_exs: :yes
250267
]
251268
""")

0 commit comments

Comments
 (0)