Current section

Files

Jump to
styler lib style inline_attrs.ex
Raw

lib/style/inline_attrs.ex

# Copyright 2025 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.InlineAttrs do
@moduledoc false
alias Styler.Style
alias Styler.Zipper
def run(zipper, ctx) do
if defstructz = Zipper.find(zipper, &match?({:defmodule, _, _}, &1)) do
body_zipper = defstructz |> Zipper.down() |> Zipper.right() |> Zipper.down() |> Zipper.down() |> Zipper.right()
{literal_attrs, others} =
body_zipper
|> Zipper.children()
|> Enum.split_with(fn
{:@, _, [{_name, _, [{:to_timeout, _, values}]}]} -> quoted_literal?(values)
{:@, _, [{name, _, values}]} -> name not in ~w(doc impl moduledoc)a and quoted_literal?(values)
_ -> false
end)
{_, hits} =
Macro.prewalk(others, %{}, fn
{:@, _, [{name, _, _}]} = ast, acc ->
def = Enum.find(literal_attrs, &match?({:@, _, [{^name, _, _}]}, &1))
val = def && {def, ast}
{ast, Map.update(acc, name, val, fn _ -> false end)}
ast, acc ->
{ast, acc}
end)
zipper =
hits
|> Enum.filter(fn {_, v} -> v end)
|> Enum.reduce(body_zipper, fn {_, {def, ast}}, zipper ->
{_, _, [{_, _, [value]}]} = def
replacement = Style.set_line(value, Style.meta(ast)[:line])
zipper
|> Zipper.find(&match?(^def, &1))
|> Zipper.remove()
|> Zipper.find(&match?(^ast, &1))
|> Zipper.replace(replacement)
|> Zipper.top()
end)
{:halt, zipper, ctx}
else
{:halt, zipper, ctx}
end
end
# Can't rely on `Macro.quoted_literal?` up front because we wrapped our literals :/
# This function is not complete, but it's good enough for the needs here.
defp quoted_literal?(value) when is_list(value) or is_map(value) do
Enum.all?(value, fn
{k, v} -> quoted_literal?(k) and quoted_literal?(v)
value -> quoted_literal?(value)
end)
end
defp quoted_literal?({:__block__, _, [value]}), do: quoted_literal?(value)
defp quoted_literal?(value), do: Macro.quoted_literal?(value)
end