Skip to content

Commit

Permalink
Add formatting pragmas.
Browse files Browse the repository at this point in the history
  • Loading branch information
kzemek committed May 1, 2024
1 parent 0f7f9ef commit 3000d8e
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 20 deletions.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,34 @@ The plugin manipulates the AST, not raw strings, so it's precise and will only c
### Reverting to the vanilla-style maps

The formatting plugin can also be used to revert all of the ES6-style map shorthand uses back to the "vanilla" style.
Set the `map_style: :vanilla` option in `.formatter.exs`, then call `mix format` to reformat your code:
Set the `es6_maps: [map_style: :vanilla]` option in `.formatter.exs`, then call `mix format` to reformat your code:

```elixir
# .formatter.exs
[
plugins: [Es6Maps.Formatter],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
map_style: :vanilla
es6_maps: [map_style: :vanilla]
]
```

### Formatting pragmas

The plugin supports pragmas in the comments to control the formatting.
The pragma must be in the form `# es6_maps: [map_style: :es6]` and can be placed anywhere in the file.
The `map_style` option can be set to `:es6` to convert to shorthand form or `:vanilla` to revert to the vanilla-style maps.
The pragma takes effect only on the line following the comment.

For example in the code below, the first map will be formatted to the shorthand form, while the second map will be left as is:

```elixir
%{foo, bar: 1} = var
# es6_maps: [map_style: :vanilla]
%{hello: hello, foo: foo, bar: 1} = var
```

`es6_maps: [map_style: :vanilla]` option in `.formatter.exs` can be combined with `# es6_maps: [map_style: :es6]` comment pragmas.

## How does it work?

`es6_maps` replaces in runtime the Elixir compiler's `elixir_map` module.
Expand Down
62 changes: 49 additions & 13 deletions lib/es6_maps/formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,37 @@ defmodule Es6Maps.Formatter do
### Reverting to the vanilla-style maps
The formatting plugin can also be used to revert all of the ES6-style map shorthand uses back to the "vanilla" style.
Set the `map_style: :vanilla` option in `.formatter.exs`, then call `mix format` to reformat your code:
Set the `es6_maps: [map_style: :vanilla]` option in `.formatter.exs`, then call `mix format` to reformat your code:
```elixir
# .formatter.exs
[
plugins: [Es6Maps.Formatter],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
map_style: :vanilla
es6_maps: [map_style: :vanilla]
]
```
### Formatting pragmas
The plugin supports pragmas in the comments to control the formatting.
The pragma must be in the form `# es6_maps: [map_style: :vanilla]` and can be placed anywhere in the file.
The `map_style` option can be set to `:es6` to convert to shorthand form or `:vanilla` to revert to the vanilla-style maps.
The pragma takes effect only on the line following the comment.
For example in the code below, the first map will be formatted to the shorthand form, while the second map will be left as is:
```elixir
%{foo, bar: 1} = var
# es6_maps: [map_style: :vanilla]
%{hello: hello, foo: foo, bar: 1} = var
```
`es6_maps: [map_style: :vanilla]` option in `.formatter.exs` can be combined with `# es6_maps: [map_style: :es6]` comment pragmas.
## Options
* `map_style` - `:es6` to convert to shorthand form, `:vanilla` to revert to the vanilla-style maps.
* `es6_maps`:
* `map_style` - `:es6` to convert to shorthand form, `:vanilla` to revert to the vanilla-style maps.
* all other options of mix format, such as `line_length`, are supported and passed down to formatting functions.
"""

Expand Down Expand Up @@ -62,8 +80,10 @@ defmodule Es6Maps.Formatter do
)
)

pragmas = comments_to_pragmas(comments)

quoted
|> Macro.postwalk(&format_map(&1, opts))
|> Macro.postwalk(&format_map(&1, pragmas, opts))
|> Code.Formatter.to_algebra(Keyword.merge([comments: comments], opts))
|> Inspect.Algebra.format(line_length)
|> case do
Expand All @@ -72,22 +92,38 @@ defmodule Es6Maps.Formatter do
end
end

defp format_map({:%{}, meta, [{:|, pipemeta, [lhs, elements]}]}, opts) do
{_, _, mapped_elements} = format_map({:%{}, pipemeta, elements}, opts)
defp comments_to_pragmas(comments) do
comments
|> Enum.filter(&String.starts_with?(&1.text, "# es6_maps: "))
|> Map.new(fn comment ->
{settings, _} =
comment.text
|> String.replace_prefix("# ", "[")
|> String.replace_suffix("", "]")
|> Code.eval_string()

{comment.line + 1, settings}
end)
end

defp format_map({:%{}, meta, [{:|, pipemeta, [lhs, elements]}]}, pragmas, opts) do
{_, _, mapped_elements} = format_map({:%{}, meta, elements}, pragmas, opts)
{:%{}, meta, [{:|, pipemeta, [lhs, mapped_elements]}]}
end

defp format_map({:%{}, _meta, _elements} = map, opts) do
case Keyword.get(opts, :map_style, :es6) do
:es6 -> format_map_es6(map, opts)
:vanilla -> format_map_vanilla(map, opts)
defp format_map({:%{}, meta, _elements} = map, pragmas, opts) do
opts = Config.Reader.merge(opts, Map.get(pragmas, meta[:line], []))

case Kernel.get_in(opts, [:es6_maps, :map_style]) || :es6 do
:es6 -> format_map_es6(map)
:vanilla -> format_map_vanilla(map)
other -> raise ArgumentError, "invalid map_style: #{inspect(other)}"
end
end

defp format_map(node, _opts), do: node
defp format_map(node, _pragmas, _opts), do: node

defp format_map_vanilla({:%{}, meta, elements}, _opts) do
defp format_map_vanilla({:%{}, meta, elements}) do
{:%{}, meta,
Enum.map(elements, fn
{key, meta, context} = var when is_atom(context) ->
Expand All @@ -98,7 +134,7 @@ defmodule Es6Maps.Formatter do
end)}
end

defp format_map_es6({:%{}, meta, elements}, _opts) do
defp format_map_es6({:%{}, meta, elements}) do
{vars, key_vals} =
Enum.reduce(elements, {[], []}, fn
{{:__block__, _, [key]}, {key, _, ctx} = var}, {vars, key_vals} when is_atom(ctx) ->
Expand Down
50 changes: 50 additions & 0 deletions test/es6_maps_test/test/format_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,56 @@ defmodule Es6MapsTest.Format do
var
end
"""

test_formatting "is not reformatted when inline-comment sets the map-style to :vanilla",
original: """
def test(var) do
%{a: a, b: b} = var
# es6_maps: [map_style: :vanilla]
%{a: a, b: b} = var
%{a: a, b: b} = var
var
end
""",
formatted: """
def test(var) do
%{a, b} = var
# es6_maps: [map_style: :vanilla]
%{a: a, b: b} = var
%{a, b} = var
var
end
"""

test_formatting "is reformatted when inline-comment sets the map-style to :es6 but default style is :vanilla",
opts: [es6_maps: [map_style: :vanilla]],
original: """
def test(var) do
%{a: a, b: b} = var
# es6_maps: [map_style: :es6]
%{a: a, b: b} = var
%{a: a, b: b} = var
var
end
""",
formatted: """
def test(var) do
%{a: a, b: b} = var
# es6_maps: [map_style: :es6]
%{a, b} = var
%{a: a, b: b} = var
var
end
""",
reverted: """
def test(var) do
%{a: a, b: b} = var
# es6_maps: [map_style: :es6]
%{a, b} = var
%{a: a, b: b} = var
var
end
"""
end

describe "map update literal" do
Expand Down
13 changes: 8 additions & 5 deletions test/es6_maps_test/test/support/formatting_assertions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ defmodule Es6MapsTest.Support.FormattingAssertions do
quote location: :keep do
test unquote(name) do
opts = unquote(opts)
original = opts |> Keyword.fetch!(:original) |> String.trim()
formatted = opts |> Keyword.get(:formatted, original) |> String.trim()
reverted = opts |> Keyword.get(:reverted, original) |> String.trim()
original = Keyword.fetch!(opts, :original)
formatted = Keyword.get(opts, :formatted, original)
reverted = Keyword.get(opts, :reverted, original)

assert Es6Maps.Formatter.format(original) == formatted
assert Es6Maps.Formatter.format(formatted, revert: true) == reverted
formatting_opts = Keyword.get(opts, :opts, [])
vanilla_opts = Config.Reader.merge(opts, es6_maps: [map_style: :vanilla])

assert Es6Maps.Formatter.format(original, formatting_opts) == formatted
assert Es6Maps.Formatter.format(formatted, vanilla_opts) == reverted
end
end
end
Expand Down

0 comments on commit 3000d8e

Please sign in to comment.