Skip to content

Commit

Permalink
hmm hmm hmm
Browse files Browse the repository at this point in the history
  • Loading branch information
novaugust committed Mar 11, 2024
1 parent fb73330 commit 3a4e85d
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 4 deletions.
91 changes: 89 additions & 2 deletions lib/style/module_directives.ex
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ defmodule Styler.Style.ModuleDirectives do
@attr_directives ~w(moduledoc shortdoc behaviour)a ++ @callback_attrs
@defstruct ~w(schema embedded_schema defstruct)a

# taken from Credo
@excluded_namespaces MapSet.new(~w(File IO Inspect Kernel Macro Supervisor Task Version)a)
@stdlib MapSet.new(~w(Access Agent Application Atom Base Behaviour
Bitwise Code Date DateTime Dict Enum Exception
File Float GenEvent GenServer HashDict HashSet
Integer IO Kernel Keyword List Macro Map MapSet
Module NaiveDateTime Node OptionParser Path Port
Process Protocol Range Record Regex Registry Set
Stream String StringIO Supervisor System Task Time
Tuple URI Version)a)

@moduledoc_false {:@, [line: nil], [{:moduledoc, [line: nil], [{:__block__, [line: nil], [false]}]}]}

def run({{:defmodule, _, children}, _} = zipper, ctx) do
Expand Down Expand Up @@ -183,10 +194,85 @@ defmodule Styler.Style.ModuleDirectives do

uses = (directives[:use] || []) |> Enum.flat_map(&expand_directive/1) |> reset_newlines()
imports = expand_and_sort(directives[:import] || [])
requires = expand_and_sort(directives[:require] || [])

all_aliases = directives[:alias] || []
aliases = expand_and_sort(all_aliases)

aliased =
aliases
|> Enum.flat_map(fn
{:alias, _, [{:__aliases__, _, aliases}]} -> [aliases]
_ -> []
end)
|> MapSet.new(&List.last/1)

excluded_first = MapSet.union(aliased, @excluded_namespaces)
excluded_last = MapSet.union(aliased, @stdlib)

to_alias =
nondirectives
|> Zipper.zip()
|> Zipper.traverse_while(%{}, fn
# Credo doesn't look at :@ nodes. I kinda like it? especially for typespecs.
{{directive, _, _}, _} = zipper, acc when directive in ~w(@ use)a ->
{:skip, zipper, acc}

# A.B.C
{{:__aliases__, _, [first, _, _ | _] = aliases}, _} = zipper, acc ->
last = List.last(aliases)
acc =
if last in excluded_last or first in excluded_first,
do: acc,
else: Map.update(acc, aliases, false, fn _ -> true end)

{:skip, zipper, acc}

zipper, acc ->
{:cont, zipper, acc}
end)
|> elem(1)
# if we have `Foo.Bar.Baz` and `Foo.Bar.Bop.Baz` both not aliased, we'll create a collision by extracting both.
# grouping by last alias lets us detect these collisions
|> Enum.group_by(fn {aliases, _} -> List.last(aliases) end)
|> Enum.filter(fn
# only extract if it doesn't conflict and was used more than once
{_, [{_, true}]} -> true
_ -> false
end)
|> MapSet.new(fn {_, [{aliases, _}]} -> aliases end)

replace_new_aliases = fn ast ->
if Enum.empty?(to_alias) do
ast
else
ast
|> Zipper.zip()
|> Zipper.traverse(fn
{{:__aliases__, meta, [_, _, _ | _] = aliases}, z_meta} = zipper ->
if aliases in to_alias, do: {{:__aliases__, meta, [List.last(aliases)]}, z_meta}, else: zipper

zipper ->
zipper
end)
|> Zipper.node()
end
end

# @TODO bug here if the first line of the parent is a comment
# diff:
# """
# -
# - # a comment on use
# use Ecto.Type
#
# + alias Some.Long.Alias
# +
# + # a comment on use
new_aliases = Enum.map(to_alias, &{:alias, [line: 0], [{:__aliases__, [last: [line: 0], line: 0], &1}]})
aliases = dbg(expand_and_sort(aliases ++ new_aliases))

requires = (directives[:require] || []) |> expand_and_sort() |> replace_new_aliases.()

directives =
[
shortdocs,
Expand All @@ -209,11 +295,12 @@ defmodule Styler.Style.ModuleDirectives do
Zipper.update(parent, &Zipper.replace_children(&1, directives))

true ->
dbg({directives, all_aliases})
parent
|> Zipper.update(&Zipper.replace_children(&1, directives))
|> Zipper.down()
|> Zipper.rightmost()
|> Zipper.insert_siblings(nondirectives)
|> Zipper.insert_siblings(replace_new_aliases.(nondirectives))
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/zipper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ defmodule Styler.Zipper do
{leftmost, %{meta | l: [], r: r}}
end

def leftmost(zipper), do: zipper
def leftmost({_, _} = zipper), do: zipper

@doc """
Returns the zipper of the right sibling of the node at this zipper, or nil.
Expand All @@ -141,7 +141,7 @@ defmodule Styler.Zipper do
{rightmost, %{meta | l: l, r: []}}
end

def rightmost(zipper), do: zipper
def rightmost({_, _} = zipper), do: zipper

@doc """
Replaces the current node in the zipper with a new node.
Expand Down

0 comments on commit 3a4e85d

Please sign in to comment.