mirror of
https://github.com/ash-project/igniter.git
synced 2024-09-20 05:22:52 +12:00
fix: detect more function call formats
fix: properly extract arguments when parsing positional args
This commit is contained in:
parent
8a6eebd6bd
commit
7ef6465da5
2 changed files with 166 additions and 65 deletions
|
@ -107,7 +107,7 @@ defmodule Igniter.Code.Function do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Moves to a function call by the given name and arity, matching the given predicate, in the current or lower scope"
|
@doc "Moves to a function call by the given name and arity, matching the given predicate, in the current or lower scope"
|
||||||
@spec move_to_function_call(Zipper.t(), atom, non_neg_integer()) ::
|
@spec move_to_function_call(Zipper.t(), atom | {atom, atom}, non_neg_integer()) ::
|
||||||
{:ok, Zipper.t()} | :error
|
{:ok, Zipper.t()} | :error
|
||||||
def move_to_function_call(zipper, name, arity, predicate \\ fn _ -> true end)
|
def move_to_function_call(zipper, name, arity, predicate \\ fn _ -> true end)
|
||||||
|
|
||||||
|
@ -135,51 +135,124 @@ defmodule Igniter.Code.Function do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Returns `true` if the node is a function call of the given name and arity"
|
@doc """
|
||||||
@spec function_call?(Zipper.t(), atom, non_neg_integer()) :: boolean()
|
Returns `true` if the node is a function call of the given name
|
||||||
def function_call?(%Zipper{} = zipper, name, arity) do
|
|
||||||
|
If an `atom` is provided, it only matches functions in the form of `function(name)`.
|
||||||
|
|
||||||
|
If an `{module, atom}` is provided, it matches functions called on the given module,
|
||||||
|
taking into account any imports or aliases.
|
||||||
|
"""
|
||||||
|
@spec function_call?(Zipper.t(), atom | {module, atom}, arity :: integer | :any) :: boolean()
|
||||||
|
def function_call?(zipper, name, arity \\ :any)
|
||||||
|
|
||||||
|
def function_call?(%Zipper{} = zipper, name, arity) when is_atom(name) do
|
||||||
zipper
|
zipper
|
||||||
|> Common.maybe_move_to_single_child_block()
|
|> Common.maybe_move_to_single_child_block()
|
||||||
|> Zipper.node()
|
|> Zipper.node()
|
||||||
|> case do
|
|> case do
|
||||||
{^name, _, args} ->
|
{^name, _, args} ->
|
||||||
Enum.count(args) == arity
|
arity == :any || Enum.count(args) == arity
|
||||||
|
|
||||||
{{^name, _, context}, _, args} when is_atom(context) ->
|
{{^name, _, context}, _, args} when is_atom(context) ->
|
||||||
Enum.count(args) == arity
|
arity == :any || Enum.count(args) == arity
|
||||||
|
|
||||||
{:|>, _, [{^name, _, context} | rest]} when is_atom(context) ->
|
{:|>, _, [{^name, _, context} | rest]} when is_atom(context) ->
|
||||||
Enum.count(rest) == arity - 1
|
arity == :any || Enum.count(rest) == arity - 1
|
||||||
|
|
||||||
{:|>, _, [^name | rest]} ->
|
{:|>, _, [^name | rest]} ->
|
||||||
Enum.count(rest) == arity - 1
|
arity == :any || Enum.count(rest) == arity - 1
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Returns `true` if the node is a function call of the given name"
|
def function_call?(%Zipper{} = zipper, {module, name}, arity) when is_atom(name) do
|
||||||
@spec function_call?(Zipper.t(), atom) :: boolean()
|
node =
|
||||||
def function_call?(%Zipper{} = zipper, name) do
|
zipper
|
||||||
zipper
|
|> Common.maybe_move_to_single_child_block()
|
||||||
|> Common.maybe_move_to_single_child_block()
|
|> Igniter.Code.Common.expand_aliases()
|
||||||
|> Zipper.node()
|
|> Zipper.node()
|
||||||
|> case do
|
|
||||||
{^name, _, _} ->
|
|
||||||
true
|
|
||||||
|
|
||||||
{{^name, _, context}, _, _} when is_atom(context) ->
|
split = module |> Module.split() |> Enum.map(&String.to_atom/1)
|
||||||
true
|
|
||||||
|
|
||||||
{:|>, _, [{^name, _, context} | _rest]} when is_atom(context) ->
|
case Igniter.Code.Common.current_env(zipper) do
|
||||||
true
|
{:ok, env} ->
|
||||||
|
imported? =
|
||||||
|
Enum.any?(env.functions ++ env.macros, fn {imported_module, funcs} ->
|
||||||
|
imported_module == module &&
|
||||||
|
Enum.any?(funcs, fn {imported_name, imported_arity} ->
|
||||||
|
name == imported_name && (arity == :any || Enum.count(imported_arity) == arity)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
{:|>, _, [^name | _rest]} ->
|
case node do
|
||||||
true
|
{{:., _, [{:__aliases__, _, ^split}, ^name]}, _, args} ->
|
||||||
|
arity == :any || Enum.count(args) == arity
|
||||||
|
|
||||||
|
{{:., _, [{:__aliases__, _, ^split}, {^name, _, context}]}, _, args}
|
||||||
|
when is_atom(context) ->
|
||||||
|
arity == :any || Enum.count(args) == arity
|
||||||
|
|
||||||
|
{:|>, _,
|
||||||
|
[
|
||||||
|
_,
|
||||||
|
{{:., _, [{:__aliases__, _, ^split}, ^name]}, _, args}
|
||||||
|
]} ->
|
||||||
|
arity == :any || Enum.count(args) == arity - 1
|
||||||
|
|
||||||
|
{:|>, _,
|
||||||
|
[
|
||||||
|
_,
|
||||||
|
{{:., _, [{:__aliases__, _, ^split}, {^name, _, context}]}, _, args}
|
||||||
|
]}
|
||||||
|
when is_atom(context) ->
|
||||||
|
arity == :any || Enum.count(args) == arity - 1
|
||||||
|
|
||||||
|
{^name, _, args} ->
|
||||||
|
imported? && (arity == :any || Enum.count(args) == arity)
|
||||||
|
|
||||||
|
{{^name, _, context}, _, args} when is_atom(context) ->
|
||||||
|
imported? && (arity == :any || Enum.count(args) == arity)
|
||||||
|
|
||||||
|
{:|>, _, [{^name, _, context} | rest]} when is_atom(context) ->
|
||||||
|
imported? && (arity == :any || Enum.count(rest) == arity - 1)
|
||||||
|
|
||||||
|
{:|>, _, [^name | rest]} ->
|
||||||
|
imported? && (arity == :any || Enum.count(rest) == arity - 1)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
false
|
case node do
|
||||||
|
{{:., _, [{:__aliases__, _, ^split}, ^name]}, _, args} ->
|
||||||
|
arity == :any || Enum.count(args) == arity
|
||||||
|
|
||||||
|
{{:., _, [{:__aliases__, _, ^split}, {^name, _, context}]}, _, args}
|
||||||
|
when is_atom(context) ->
|
||||||
|
arity == :any || Enum.count(args) == arity
|
||||||
|
|
||||||
|
{:|>, _,
|
||||||
|
[
|
||||||
|
_,
|
||||||
|
{{:., _, [{:__aliases__, _, ^split}, ^name]}, _, args}
|
||||||
|
]} ->
|
||||||
|
arity == :any || Enum.count(args) == arity - 1
|
||||||
|
|
||||||
|
{:|>, _,
|
||||||
|
[
|
||||||
|
_,
|
||||||
|
{{:., _, [{:__aliases__, _, ^split}, {^name, _, context}]}, _, args}
|
||||||
|
]}
|
||||||
|
when is_atom(context) ->
|
||||||
|
arity == :any || Enum.count(args) == arity - 1
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -190,6 +263,22 @@ defmodule Igniter.Code.Function do
|
||||||
|> Common.maybe_move_to_single_child_block()
|
|> Common.maybe_move_to_single_child_block()
|
||||||
|> Zipper.node()
|
|> Zipper.node()
|
||||||
|> case do
|
|> case do
|
||||||
|
{:|>, _,
|
||||||
|
[
|
||||||
|
_,
|
||||||
|
{{:., _, [_, name]}, _, _}
|
||||||
|
]}
|
||||||
|
when is_atom(name) ->
|
||||||
|
true
|
||||||
|
|
||||||
|
{:|>, _,
|
||||||
|
[
|
||||||
|
_,
|
||||||
|
{{:., _, [_, {name, _, context}]}, _, _args}
|
||||||
|
]}
|
||||||
|
when is_atom(name) and is_atom(context) ->
|
||||||
|
true
|
||||||
|
|
||||||
{:|>, _, [{name, _, context} | _rest]} when is_atom(context) and is_atom(name) ->
|
{:|>, _, [{name, _, context} | _rest]} when is_atom(context) and is_atom(name) ->
|
||||||
true
|
true
|
||||||
|
|
||||||
|
@ -202,6 +291,13 @@ defmodule Igniter.Code.Function do
|
||||||
{{name, _, context}, _, _} when is_atom(context) and is_atom(name) ->
|
{{name, _, context}, _, _} when is_atom(context) and is_atom(name) ->
|
||||||
true
|
true
|
||||||
|
|
||||||
|
{{:., _, [_, name]}, _, _} when is_atom(name) ->
|
||||||
|
true
|
||||||
|
|
||||||
|
{{:., _, [_, {name, _, context}]}, _, _}
|
||||||
|
when is_atom(name) and is_atom(context) ->
|
||||||
|
true
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -404,43 +500,15 @@ defmodule Igniter.Code.Function do
|
||||||
else
|
else
|
||||||
zipper
|
zipper
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> case do
|
|> Zipper.right()
|
||||||
nil ->
|
|> argument_matches_predicate?(index - 1, func)
|
||||||
nil
|
|
||||||
|
|
||||||
zipper ->
|
|
||||||
zipper
|
|
||||||
|> Zipper.rightmost()
|
|
||||||
|> Zipper.down()
|
|
||||||
|> case do
|
|
||||||
nil ->
|
|
||||||
nil
|
|
||||||
|
|
||||||
zipper ->
|
|
||||||
zipper
|
|
||||||
|> Common.nth_right(index - 1)
|
|
||||||
|> case do
|
|
||||||
:error ->
|
|
||||||
false
|
|
||||||
|
|
||||||
{:ok, zipper} ->
|
|
||||||
zipper
|
|
||||||
|> Common.maybe_move_to_single_child_block()
|
|
||||||
|> func.()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
zipper
|
case Zipper.node(zipper) do
|
||||||
|> Zipper.down()
|
{{:., _, [_mod, name]}, _, args} when is_atom(name) and is_list(args) ->
|
||||||
|> case do
|
|
||||||
nil ->
|
|
||||||
false
|
|
||||||
|
|
||||||
zipper ->
|
|
||||||
zipper
|
zipper
|
||||||
|> Common.nth_right(index)
|
|> Zipper.down()
|
||||||
|
|> Common.nth_right(index + 1)
|
||||||
|> case do
|
|> case do
|
||||||
:error ->
|
:error ->
|
||||||
false
|
false
|
||||||
|
@ -450,6 +518,27 @@ defmodule Igniter.Code.Function do
|
||||||
|> Common.maybe_move_to_single_child_block()
|
|> Common.maybe_move_to_single_child_block()
|
||||||
|> func.()
|
|> func.()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
zipper
|
||||||
|
|> Zipper.down()
|
||||||
|
|> case do
|
||||||
|
nil ->
|
||||||
|
false
|
||||||
|
|
||||||
|
zipper ->
|
||||||
|
zipper
|
||||||
|
|> Common.nth_right(index)
|
||||||
|
|> case do
|
||||||
|
:error ->
|
||||||
|
false
|
||||||
|
|
||||||
|
{:ok, zipper} ->
|
||||||
|
zipper
|
||||||
|
|> Common.maybe_move_to_single_child_block()
|
||||||
|
|> func.()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
|
@ -194,7 +194,7 @@ defmodule Igniter.Mix.Task do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{Igniter.Mix.Task.add_default_values(Map.new(got), desired), positional}
|
{Igniter.Mix.Task.add_default_values(Map.new(got), desired), argv}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -254,14 +254,26 @@ defmodule Igniter.Mix.Task do
|
||||||
|
|
||||||
def extract_positional_args(argv, got_argv, positional) do
|
def extract_positional_args(argv, got_argv, positional) do
|
||||||
case OptionParser.next(argv, switches: []) do
|
case OptionParser.next(argv, switches: []) do
|
||||||
{:ok, key, value, rest} ->
|
{:ok, _key, _value, rest} ->
|
||||||
extract_positional_args(rest, got_argv ++ [{key, value}], positional)
|
extract_positional_args(
|
||||||
|
rest,
|
||||||
|
got_argv ++ [Enum.at(argv, 0), Enum.at(argv, 1)],
|
||||||
|
positional
|
||||||
|
)
|
||||||
|
|
||||||
{:invalid, key, value, rest} ->
|
{:invalid, _key, _value, rest} ->
|
||||||
extract_positional_args(rest, got_argv ++ [{key, value}], positional)
|
extract_positional_args(
|
||||||
|
rest,
|
||||||
|
got_argv ++ [Enum.at(argv, 0), Enum.at(argv, 1)],
|
||||||
|
positional
|
||||||
|
)
|
||||||
|
|
||||||
{:undefined, key, value, rest} ->
|
{:undefined, _key, _value, rest} ->
|
||||||
extract_positional_args(rest, got_argv ++ [{key, value}], positional)
|
extract_positional_args(
|
||||||
|
rest,
|
||||||
|
got_argv ++ [Enum.at(argv, 0), Enum.at(argv, 1)],
|
||||||
|
positional
|
||||||
|
)
|
||||||
|
|
||||||
{:error, rest} ->
|
{:error, rest} ->
|
||||||
[first | rest] = rest
|
[first | rest] = rest
|
||||||
|
|
Loading…
Reference in a new issue