Packages
igniter
0.1.7
0.8.2
0.8.1
0.8.0
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.30
0.6.29
0.6.28
0.6.27
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.52
0.5.51
0.5.50
0.5.49
0.5.48
0.5.47
0.5.46
0.5.45
0.5.44
0.5.43
0.5.42
0.5.41
0.5.40
0.5.39
0.5.38
0.5.37
0.5.36
0.5.35
0.5.34
0.5.33
0.5.32
0.5.31
0.5.30
0.5.29
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.78
0.3.77
0.3.76
0.3.75
0.3.74
0.3.73
0.3.72
0.3.71
0.3.70
0.3.69
0.3.68
0.3.67
0.3.66
0.3.65
0.3.64
0.3.63
0.3.62
0.3.61
0.3.60
0.3.59
0.3.58
0.3.57
0.3.56
0.3.55
0.3.54
0.3.53
0.3.52
0.3.51
0.3.50
0.3.49
0.3.48
0.3.47
0.3.46
0.3.45
0.3.44
0.3.43
0.3.42
0.3.41
0.3.40
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A code generation and project patching framework
Current section
Files
Jump to
Current section
Files
lib/code/module.ex
defmodule Igniter.Code.Module do
@moduledoc "Utilities for working with Elixir modules"
require Igniter.Code.Common
alias Igniter.Code.Common
alias Sourceror.Zipper
@doc "Given a suffix, returns a module name with the prefix of the current project"
@spec module_name(String.t()) :: module()
def module_name(suffix) do
Module.concat(module_name_prefix(), suffix)
end
@doc "Returns the idiomatic file location for a given module"
@spec proper_location(module()) :: Path.t()
def proper_location(module_name) do
path =
module_name
|> Module.split()
|> Enum.map(&to_string/1)
|> Enum.map(&Macro.underscore/1)
last = List.last(path)
leading = :lists.droplast(path)
Path.join(["lib" | leading] ++ ["#{last}.ex"])
end
def module?(zipper) do
Common.node_matches_pattern?(zipper, {:__aliases__, _, [_ | _]})
end
@doc "Parses a string into a module name"
@spec parse(String.t()) :: module()
def parse(module_name) do
module_name
|> String.split(".")
|> Module.concat()
end
@doc "The module name prefix based on the mix project's module name"
@spec module_name_prefix() :: module()
def module_name_prefix do
Mix.Project.get!()
|> Module.split()
|> :lists.droplast()
|> Module.concat()
end
@doc "Moves the zipper to a defmodule call"
@spec move_to_defmodule(Zipper.t()) :: {:ok, Zipper.t()} | :error
def move_to_defmodule(zipper) do
Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, :defmodule, 2)
end
# sobelow_skip ["DOS.StringToAtom"]
@doc "Moves the zipper to the body of a module that `use`s the provided module (or one of the provided modules)."
@spec move_to_module_using(Zipper.t(), module | list(module)) :: {:ok, Zipper.t()} | :error
def move_to_module_using(zipper, [module]) do
move_to_module_using(zipper, module)
end
def move_to_module_using(zipper, [module | rest] = one_of_modules)
when is_list(one_of_modules) do
case move_to_module_using(zipper, module) do
{:ok, zipper} ->
{:ok, zipper}
:error ->
move_to_module_using(zipper, rest)
end
end
# sobelow_skip ["DOS.StringToAtom"]
def move_to_module_using(zipper, module) do
split_module =
module
|> Module.split()
|> Enum.map(&String.to_atom/1)
with {:ok, zipper} <- Common.move_to_pattern(zipper, {:defmodule, _, [_, _]}),
subtree <- Zipper.subtree(zipper),
subtree <- subtree |> Zipper.down() |> Zipper.rightmost(),
subtree <- remove_module_definitions(subtree),
{:ok, _found} <-
Common.move_to(subtree, fn
{:use, _, [^module | _]} ->
true
{:use, _, [{:__aliases__, _, ^split_module} | _]} ->
true
end) do
Common.move_to_do_block(zipper)
else
_ ->
:error
end
end
@doc "Moves the zipper to the `use` statement for a provided module."
def move_to_use(zipper, module) do
Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, :use, [1, 2], fn call ->
Igniter.Code.Function.argument_matches_predicate?(
call,
0,
&Igniter.Code.Common.nodes_equal?(&1, module)
)
end)
end
def move_to_defp(zipper, fun, arity) do
do_move_to_def(zipper, fun, arity, :defp)
end
def move_to_def(zipper, fun, arity) do
do_move_to_def(zipper, fun, arity, :def)
end
defp do_move_to_def(zipper, fun, arity, kind) do
case Common.move_to_pattern(
zipper,
{^kind, _, [{^fun, _, args}, _]} when length(args) == arity
) do
:error ->
if arity == 0 do
case Common.move_to_pattern(
zipper,
{^kind, _, [{^fun, _, context}, _]} when is_atom(context)
) do
:error ->
:error
{:ok, zipper} ->
Common.move_to_do_block(zipper)
end
else
:error
end
{:ok, zipper} ->
Common.move_to_do_block(zipper)
end
end
defp remove_module_definitions(zipper) do
Zipper.traverse(zipper, fn
{:defmodule, _, _} ->
Zipper.remove(zipper)
other ->
other
end)
end
end