Packages
igniter
0.5.15
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/igniter/code/list.ex
defmodule Igniter.Code.List do
@moduledoc """
Utilities for working with lists.
"""
require Igniter.Code.Common
alias Igniter.Code.Common
alias Sourceror.Zipper
@type equality_pred :: (Zipper.t(), Macro.t() -> boolean)
@doc "Returns true if the `zipper` is at a list literal"
@spec list?(Zipper.t()) :: boolean()
def list?(zipper) do
Common.node_matches_pattern?(zipper, value when is_list(value))
end
@doc "Prepends `quoted` to the list unless it is already present, determined by `equality_pred`."
@spec prepend_new_to_list(Zipper.t(), quoted :: Macro.t(), equality_pred) ::
{:ok, Zipper.t()} | :error
def prepend_new_to_list(zipper, quoted, equality_pred \\ &Common.nodes_equal?/2) do
Common.within(zipper, fn zipper ->
if list?(zipper) do
zipper
|> find_list_item_index(fn value ->
equality_pred.(value, quoted)
end)
|> case do
nil ->
prepend_to_list(zipper, quoted)
_ ->
{:ok, zipper}
end
else
:error
end
end)
end
@doc "Appends `quoted` to the list unless it is already present, determined by `equality_pred`."
@spec append_new_to_list(Zipper.t(), quoted :: Macro.t(), equality_pred) ::
{:ok, Zipper.t()} | :error
def append_new_to_list(zipper, quoted, equality_pred \\ &Common.nodes_equal?/2) do
if list?(zipper) do
zipper
|> find_list_item_index(fn value ->
equality_pred.(value, quoted)
end)
|> case do
nil ->
append_to_list(zipper, quoted)
_ ->
{:ok, zipper}
end
else
:error
end
end
@doc "Prepends `quoted` to the list"
@spec prepend_to_list(Zipper.t(), quoted :: Macro.t()) :: {:ok, Zipper.t()} | :error
def prepend_to_list(zipper, quoted) do
if list?(zipper) do
{:ok,
zipper
|> Common.maybe_move_to_single_child_block()
|> Zipper.insert_child(quoted)}
else
:error
end
end
@doc "Appends `quoted` to the list"
@spec append_to_list(Zipper.t(), quoted :: Macro.t()) :: {:ok, Zipper.t()} | :error
def append_to_list(zipper, quoted) do
if list?(zipper) do
{:ok,
zipper
|> Common.maybe_move_to_single_child_block()
|> Zipper.append_child(quoted)}
else
:error
end
end
@spec remove_from_list(Zipper.t(), predicate :: (Zipper.t() -> boolean())) ::
{:ok, Zipper.t()} | :error
def remove_from_list(zipper, predicate) do
if list?(zipper) do
Common.within(zipper, fn zipper ->
zipper
|> Zipper.down()
|> case do
nil ->
{:ok, zipper}
zipper ->
zipper
|> Common.move_right(predicate)
|> case do
:error ->
:error
{:ok, zipper} ->
{:ok, Zipper.remove(zipper)}
end
end
end)
|> case do
:error ->
{:ok, zipper}
{:ok, ^zipper} ->
{:ok, zipper}
{:ok, zipper} ->
remove_from_list(zipper, predicate)
end
else
:error
end
end
@spec replace_in_list(Zipper.t(), predicate :: (Zipper.t() -> boolean()), term :: any()) ::
{:ok, Zipper.t()} | :error
def replace_in_list(zipper, predicate, value) do
if list?(zipper) do
Common.within(zipper, fn zipper ->
zipper
|> Zipper.down()
|> Common.move_right(predicate)
|> case do
:error ->
:error
{:ok, zipper} ->
{:ok, Igniter.Code.Common.replace_code(zipper, value)}
end
end)
|> case do
:error ->
{:ok, zipper}
{:ok, zipper} ->
replace_in_list(zipper, predicate, value)
end
else
:error
end
end
@doc "Removes the item at the given index, returning `:error` if nothing is at that index"
@spec remove_index(Zipper.t(), index :: non_neg_integer()) :: {:ok, Zipper.t()} | :error
def remove_index(zipper, index) do
if list?(zipper) do
Common.within(zipper, fn zipper ->
zipper
|> Igniter.Code.Common.maybe_move_to_single_child_block()
|> Zipper.down()
|> Common.move_right(index)
|> case do
:error ->
:error
{:ok, zipper} ->
{:ok, Zipper.remove(zipper)}
end
end)
else
:error
end
end
@doc "Finds the index of the first list item that satisfies `pred`"
@spec find_list_item_index(Zipper.t(), (Zipper.t() -> boolean())) :: integer() | nil
def find_list_item_index(zipper, pred) do
# go into first list item
zipper
|> Common.maybe_move_to_single_child_block()
|> Zipper.down()
|> case do
nil ->
nil
zipper ->
find_index_right(zipper, pred, 0)
end
end
@doc "Moves to the list item matching the given predicate"
@spec move_to_list_item(Zipper.t(), (Zipper.t() -> boolean())) :: {:ok, Zipper.t()} | :error
def move_to_list_item(zipper, pred) do
# go into first list item
zipper
|> Common.maybe_move_to_single_child_block()
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
do_move_to_list_item(zipper, pred)
end
end
@doc "Moves to the list item matching the given predicate"
@spec map(Zipper.t(), (Zipper.t() -> {:ok, Zipper.t()})) :: {:ok, Zipper.t()} | :error
def map(zipper, fun) do
# go into first list item
zipper
|> Common.maybe_move_to_single_child_block()
|> Zipper.down()
|> case do
nil ->
:error
zipper ->
do_map(zipper, fun)
end
end
defp do_map(zipper, fun) do
case Igniter.Code.Common.within(zipper, fun) do
{:ok, zipper} ->
case Zipper.right(zipper) do
nil ->
{:ok, zipper}
right ->
do_map(right, fun)
end
:error ->
:error
end
end
@doc "Moves to the list item matching the given predicate, assuming you are currently inside the list"
def do_move_to_list_item(zipper, pred) do
if pred.(zipper) do
{:ok, zipper}
else
zipper
|> Zipper.right()
|> case do
nil ->
:error
right ->
do_move_to_list_item(right, pred)
end
end
end
defp find_index_right(zipper, pred, index) do
if pred.(Common.maybe_move_to_single_child_block(zipper)) do
index
else
case Zipper.right(zipper) do
nil ->
nil
zipper ->
zipper
|> find_index_right(pred, index + 1)
end
end
end
end