Packages
styler
1.12.0
1.12.0
1.11.0
1.10.1
1.10.0
1.9.1
1.9.0
1.8.0
1.7.0
1.6.0
1.5.1
1.5.0
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
1.0.0-alpha.0
0.11.9
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.5
0.10.4
retired
0.10.3
0.10.2
0.10.1
0.10.0
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
retired
0.9.1
retired
0.9.0
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.1
0.3.0
0.2.0
0.1.1
0.1.0
A code-style enforcer that will just FIFY instead of complaining
Current section
Files
Jump to
Current section
Files
lib/style/configs.ex
# Copyright 2024 Adobe. All rights reserved.
# This file is licensed to you under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
# OF ANY KIND, either express or implied. See the License for the specific language
# governing permissions and limitations under the License.
defmodule Styler.Style.Configs do
@moduledoc """
Orders `Config.config/2,3` stanzas in configuration files.
- ordering is done only within immediate-sibling config statements
- assignments are moved above the configuration blocks
- any non `config/2,3` or assignment (`=/2`) calls mark the end of a sorting block.
this is support having conditional blocks (`if/case/cond`) and `import_config` stanzas between blocks
### Breakages
If you configure the same values multiple times, Styler may swap their orders
**Before**
line 04: config :foo, bar: :zab
line 40: config :foo, bar: :baz
# Application.fetch_env!(:foo)[:bar] => :baz
**After**
line 04: config :foo, bar: :baz
line 05: config :foo, bar: :zab
# Application.fetch_env!(:foo)[:bar] => :zab
**Fix**
The reason Styler sorts configuration is to help you noticed these duplicated configuration stanzas.
Delete the duplicative/erroneous stanza and life will be good.
"""
alias Styler.Style
def run({{:import, _, [{:__aliases__, _, [:Config]}]}, _} = zipper, %{config?: true} = ctx) do
{:skip, zipper, Map.put(ctx, :mix_config?, true)}
end
def run({{:config, cfm, [_, _ | _]} = config, zm}, %{mix_config?: true, comments: comments} = ctx) do
# all of these list are reversed due to the reduce
{configs, assignments, rest} = accumulate(zm.r, [], [])
configs =
[config | configs]
|> Enum.group_by(fn
{:config, _, [{:__block__, _, [app]} | _]} -> app
{:config, _, [arg | _]} -> Style.without_meta(arg)
end)
|> Enum.sort()
|> Enum.flat_map(fn {_app, configs} ->
configs
|> Enum.sort_by(&Style.without_meta/1)
|> Style.reset_newlines()
end)
nodes =
assignments
|> Enum.reverse()
|> Style.reset_newlines()
|> Enum.concat(configs)
{nodes, comments, rest} =
if changed?(nodes) do
# config line is the first line to be used as part of this block
{node_comments, _} = Style.comments_for_node(config, comments)
first_line = min(List.first(node_comments)[:line] || cfm[:line], cfm[:line])
# Sorting and re-spacing can make the block taller (config groups gain blank lines between them).
# reodering means the block can grow past `rest` and its comments, causing the comments for `rest` to get sucked up into our block.
max_before_ordering = [config | configs ++ assignments] |> Enum.map(&Style.max_line/1) |> Enum.max()
{block_comments, tail_comments} = Enum.split_while(comments, &(&1.line <= max_before_ordering))
{nodes, block_comments} = Style.order_line_meta_and_comments(nodes, block_comments, first_line)
delta = Style.max_line(nodes) - max_before_ordering
tail_comments = Enum.map(tail_comments, &%{&1 | line: &1.line + delta})
rest = Style.shift_line(rest, delta)
{nodes, Enum.sort_by(block_comments ++ tail_comments, & &1.line), rest}
else
{nodes, comments, rest}
end
[config | left_siblings] = Enum.reverse(nodes, zm.l)
{:skip, {config, %{zm | l: left_siblings, r: rest}}, %{ctx | comments: comments}}
end
def run(zipper, %{config?: true} = ctx) do
{:cont, zipper, ctx}
end
def run(zipper, %{file: file} = ctx) do
if file =~ ~r|config/.*\.exs| or file =~ ~r|rel/overlays/.*\.exs| do
# @TODO have this run forward to `import Config`, then run forward from there until we find `config` itself. no need for multi function head
{:cont, zipper, Map.put(ctx, :config?, true)}
else
{:halt, zipper, ctx}
end
end
defp changed?([{_, am, _}, {_, bm, _} = b | tail]) do
if am[:line] > bm[:line], do: true, else: changed?([b | tail])
end
defp changed?(_), do: false
defp accumulate([{:config, _, [_, _ | _]} = c | siblings], cs, as), do: accumulate(siblings, [c | cs], as)
defp accumulate([{:=, _, [_lhs, _rhs]} = a | siblings], cs, as), do: accumulate(siblings, cs, [a | as])
defp accumulate(rest, configs, assignments), do: {configs, assignments, rest}
end