refactor: simplify zipper usage (#45)

* build: add otp version to elixir in .tool-versions

* refactor: don't make subtree zippers unless needed in `Igniter.Code.Common`

* refactor: internal function in `Igniter.Code.Common`

* docs: add docstring for `Igniter.Code.Common.use_aliases/2`

* refactor: simplify zipper usage

Refactored using these two general rules:

1. Don't make subtrees unnecessarily

    zipper
    |> Zipper.subtree()
    |> Zipper.root()

    # is equivalent to
    zipper
    |> Zipper.node()

2. Access `zipper.node` when appropriate

    zipper
    |> Zipper.node()
    |> case do
      ...
    end

    # is equivalent to
    case zipper.node do
      ...
    end

* fix: remove redundant case clause in `Igniter.Code.Common`
This commit is contained in:
Zach Allaun 2024-07-11 11:25:20 -07:00 committed by GitHub
parent 772a916684
commit 43a2f39943
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 49 additions and 135 deletions

View file

@ -1,2 +1,2 @@
erlang 26.0.2
elixir 1.17.1
elixir 1.17.1-otp-26

View file

@ -61,8 +61,7 @@ defmodule Igniter.Code.Common do
ast =
unquote(zipper)
|> Igniter.Code.Common.maybe_move_to_single_child_block()
|> Zipper.subtree()
|> Zipper.root()
|> Zipper.node()
match?(unquote(pattern), ast)
end
@ -133,20 +132,14 @@ defmodule Igniter.Code.Common do
end
defp do_add_code(zipper, new_code, placement, expand_env? \\ true) do
current_code =
zipper
|> Zipper.subtree()
new_code =
if expand_env? do
use_aliases(new_code, current_code)
use_aliases(new_code, zipper)
else
new_code
end
current_code = Zipper.root(current_code)
case current_code do
case zipper.node do
{:__block__, meta, stuff} when length(stuff) > 1 or stuff == [] ->
new_stuff =
if placement == :after do
@ -161,19 +154,7 @@ defmodule Igniter.Code.Common do
zipper
|> highest_adjacent_block()
|> case do
nil ->
if placement == :after do
Zipper.replace(zipper, {:__block__, [], [code, new_code]})
else
Zipper.replace(zipper, {:__block__, [], [new_code, code]})
end
upwards ->
upwards
|> Zipper.subtree()
|> Zipper.root()
|> case do
{:__block__, meta, stuff} ->
%Zipper{node: {:__block__, meta, stuff}} = upwards ->
new_stuff =
if placement == :after do
List.wrap(stuff) ++ [new_code]
@ -198,43 +179,30 @@ defmodule Igniter.Code.Common do
end
end
end
end
def replace_code(zipper, code) when is_binary(code) do
add_code(zipper, Sourceror.parse_string!(code))
end
def replace_code(zipper, code) do
current_code =
zipper
|> Zipper.subtree()
code = use_aliases(code, current_code)
code = use_aliases(code, zipper)
Zipper.replace(zipper, code)
end
defp highest_adjacent_block(zipper) do
case Zipper.up(zipper) do
nil ->
nil
upwards ->
upwards
|> Zipper.node()
|> case do
{:__block__, _, _} ->
case highest_adjacent_block(upwards) do
nil -> upwards
zipper -> zipper
end
%Zipper{node: {:__block__, _, _}} = upwards ->
highest_adjacent_block(upwards) || upwards
_ ->
nil
end
end
end
@doc """
Replaces full module names in `new_code` with any aliases for that
module found in the `current_code` environment.
"""
def use_aliases(new_code, current_code) do
case current_env(current_code) do
{:ok, env} ->
@ -333,10 +301,7 @@ defmodule Igniter.Code.Common do
def maybe_move_to_single_child_block(nil), do: nil
def maybe_move_to_single_child_block(zipper) do
zipper
|> Zipper.subtree()
|> Zipper.root()
|> case do
case zipper.node do
{:__block__, _, [_]} ->
zipper
|> Zipper.down()
@ -360,10 +325,7 @@ defmodule Igniter.Code.Common do
def maybe_move_to_block(nil), do: nil
def maybe_move_to_block(zipper) do
zipper
|> Zipper.subtree()
|> Zipper.root()
|> case do
case zipper.node do
{:__block__, _, _} ->
zipper
|> Zipper.down()
@ -550,7 +512,6 @@ defmodule Igniter.Code.Common do
zipper
|> Igniter.Code.Common.move_to_cursor(pattern)
|> Zipper.subtree()
|> Zipper.node()
# => 10
```
@ -569,7 +530,7 @@ defmodule Igniter.Code.Common do
defp do_move_to_cursor(%Zipper{} = zipper, %Zipper{} = pattern_zipper) do
cond do
cursor?(pattern_zipper |> Zipper.subtree() |> Zipper.node()) ->
pattern_zipper |> Zipper.node() |> cursor?() ->
{:ok, zipper}
match_type = zippers_match(zipper, pattern_zipper) ->
@ -593,17 +554,7 @@ defmodule Igniter.Code.Common do
defp cursor?(_other), do: false
defp zippers_match(zipper, pattern_zipper) do
zipper_node =
zipper
|> Zipper.subtree()
|> Zipper.node()
pattern_node =
pattern_zipper
|> Zipper.subtree()
|> Zipper.node()
case {zipper_node, pattern_node} do
case {zipper.node, pattern_zipper.node} do
{_, {:__, _, _}} ->
:skip
@ -673,8 +624,7 @@ defmodule Igniter.Code.Common do
@spec nodes_equal?(Zipper.t() | Macro.t(), Macro.t()) :: boolean
def nodes_equal?(%Zipper{} = left, right) do
with zipper when not is_nil(zipper) <- Zipper.up(left),
{:defmodule, _, [{:__aliases__, _, parts}, _]} <-
zipper |> Zipper.subtree() |> Zipper.node(),
{:defmodule, _, [{:__aliases__, _, parts}, _]} <- Zipper.node(zipper),
{:ok, env} <- current_env(zipper),
true <- nodes_equal?({:__aliases__, [], [Module.concat([env.module | parts])]}, right) do
true
@ -682,7 +632,6 @@ defmodule Igniter.Code.Common do
_ ->
left
|> expand_aliases()
|> Zipper.subtree()
|> Zipper.node()
|> nodes_equal?(right)
end
@ -700,10 +649,7 @@ defmodule Igniter.Code.Common do
@spec expand_alias(Zipper.t()) :: Zipper.t()
def expand_alias(zipper) do
zipper
|> Zipper.subtree()
|> Zipper.node()
|> case do
case zipper.node do
{:__aliases__, _, parts} ->
case current_env(zipper) do
{:ok, env} ->

View file

@ -13,12 +13,7 @@ defmodule Igniter.Code.Function do
unquote(zipper),
unquote(index),
fn zipper ->
code_at_node =
zipper
|> Sourceror.Zipper.subtree()
|> Sourceror.Zipper.root()
match?(unquote(pattern), code_at_node)
match?(unquote(pattern), zipper.node)
end
)
end
@ -87,8 +82,7 @@ defmodule Igniter.Code.Function do
def function_call?(%Zipper{} = zipper, name, arity) do
zipper
|> Common.maybe_move_to_single_child_block()
|> Zipper.subtree()
|> Zipper.root()
|> Zipper.node()
|> case do
{^name, _, args} ->
Enum.count(args) == arity
@ -112,8 +106,7 @@ defmodule Igniter.Code.Function do
def function_call?(%Zipper{} = zipper, name) do
zipper
|> Common.maybe_move_to_single_child_block()
|> Zipper.subtree()
|> Zipper.root()
|> Zipper.node()
|> case do
{^name, _, _} ->
true
@ -137,8 +130,7 @@ defmodule Igniter.Code.Function do
def function_call?(%Zipper{} = zipper) do
zipper
|> Common.maybe_move_to_single_child_block()
|> Zipper.subtree()
|> Zipper.root()
|> Zipper.node()
|> case do
{:|>, _, [{name, _, context} | _rest]} when is_atom(context) and is_atom(name) ->
true
@ -391,10 +383,7 @@ defmodule Igniter.Code.Function do
end
defp pipeline?(zipper) do
zipper
|> Zipper.subtree()
|> Zipper.root()
|> case do
case zipper.node do
{:|>, _, _} -> true
_ -> false
end

View file

@ -117,10 +117,7 @@ defmodule Igniter.Code.Keyword do
value = Common.use_aliases(value, zipper)
to_append =
zipper
|> Zipper.subtree()
|> Zipper.node()
|> case do
case zipper.node do
[{{:__block__, meta, _}, _} | _] ->
if meta[:format] do
{{:__block__, [format: meta[:format]], [key]}, {:__block__, [], [value]}}
@ -172,10 +169,7 @@ defmodule Igniter.Code.Keyword do
end) do
:error ->
to_append =
zipper
|> Zipper.subtree()
|> Zipper.node()
|> case do
case zipper.node do
[{{:__block__, meta, _}, {:__block__, _, _}} | _] ->
value =
value

View file

@ -136,10 +136,7 @@ defmodule Igniter.Code.Map do
end
defp map_keys_format(zipper) do
zipper
|> Zipper.subtree()
|> Zipper.node()
|> case do
case zipper.node do
value when is_list(value) ->
Enum.all?(value, fn
{:__block__, meta, _} ->

View file

@ -156,17 +156,13 @@ defmodule Igniter.Code.Module do
|> Rewrite.Source.get(:quoted)
|> Zipper.zip()
|> Zipper.traverse([], fn zipper, acc ->
zipper
|> Zipper.subtree()
|> Zipper.node()
|> case do
case zipper.node do
{:defmodule, _, [_, _]} ->
{:ok, mod_zipper} = Igniter.Code.Function.move_to_nth_argument(zipper, 0)
module_name =
mod_zipper
|> Igniter.Code.Common.expand_alias()
|> Zipper.subtree()
|> Zipper.node()
|> Igniter.Code.Module.to_module_name()
@ -265,7 +261,6 @@ defmodule Igniter.Code.Module do
module <-
zipper
|> Igniter.Code.Common.expand_alias()
|> Zipper.subtree()
|> Zipper.node(),
module when not is_nil(module) <- to_module_name(module),
new_path when not is_nil(new_path) <-

View file

@ -8,10 +8,7 @@ defmodule Igniter.Code.Tuple do
@doc "Returns `true` if the zipper is at a literal tuple, `false` if not."
@spec tuple?(Zipper.t()) :: boolean()
def tuple?(item) do
item
|> Zipper.subtree()
|> Zipper.root()
|> case do
case item.node do
{:{}, _, _} -> true
{_, _} -> true
_ -> false

View file

@ -89,7 +89,6 @@ defmodule Igniter.Project.Deps do
end
end) do
current_declaration
|> Zipper.subtree()
|> Zipper.node()
|> Sourceror.to_string()
else

View file

@ -5,8 +5,7 @@ defmodule Igniter.Util.Debug do
@doc "Puts the formatted code at the node of the zipper to the console"
def puts_code_at_node(zipper) do
zipper
|> Zipper.subtree()
|> Zipper.root()
|> Zipper.node()
|> Sourceror.to_string()
|> then(&"==code==\n#{&1}\n==code==\n")
|> IO.puts()
@ -17,16 +16,14 @@ defmodule Igniter.Util.Debug do
@doc "Returns the formatted code at the node of the zipper to the console"
def code_at_node(zipper) do
zipper
|> Zipper.subtree()
|> Zipper.root()
|> Zipper.node()
|> Sourceror.to_string()
end
@doc "Puts the ast at the node of the zipper to the console"
def puts_ast_at_node(zipper) do
zipper
|> Zipper.subtree()
|> Zipper.root()
|> Zipper.node()
|> then(&"==ast==\n#{inspect(&1, pretty: true)}\n==ast==\n")
|> IO.puts()