View Source Sourceror.Zipper (Sourceror v1.7.1)
Tree-like data structure that provides enhanced navigation and modification of an Elixir AST.
This implementation is based on Gérard Huet Functional pearl: the zipper
and Clojure's clojure.zip
API.
A zipper is a data structure that represents a location in a tree from the perspective of the current node, also called the focus.
It is represented by a struct containing a :node
and :path
, which is a
private field used to track the position of the :node
with regards to
the entire tree. The :path
is an implementation detail that should be
considered private.
For more information and examples, see the following guides:
- Zippers (an introduction)
- Expand multi-alias syntax (an example)
Summary
Functions
Inserts the child
as the rightmost child
of the node
at this zipper
,
without moving.
Creates a zipper
from a tree node
focused at the innermost descendant containing position
.
Returns a list of children of the node
. Returns nil
if the node is a leaf.
Returns the zipper
of the leftmost child of the node
at this zipper
, or
nil
if there's no children.
Returns a zipper
to the node
that satisfies the predicate
function, or
nil
if none is found.
Returns a list of zippers
to each node
that satisfies the predicate
function, or
an empty list if none are found.
Like find/3
, but returns the first non-falsy value returned by fun
.
Inserts the child
as the leftmost child
of the node
at this zipper
,
without moving.
Inserts the child
as the left sibling of the node
at this zipper
,
without moving. Raises an ArgumentError
when attempting to insert a sibling
at the top level.
Inserts the child
as the right sibling of the node
at this zipper
,
without moving. Raises an ArgumentError
when attempting to insert a sibling
at the top level.
Returns the zipper
of the left sibling of the node
at this zipper
, or
nil
.
Returns the leftmost sibling of the node
at this zipper
, or itself.
Returns a new branch node
, given an existing node
and new children
.
Matches zipper
against the given pattern, moving to the location of __cursor__()
.
Returns the following zipper
in depth-first pre-order.
Returns the node
at the zipper
.
Returns the previous zipper
in depth-first pre-order. If it's already at
the end, it returns nil
.
Removes the node
at the zipper, returning the zipper
that would have
preceded it in a depth-first walk. Raises an ArgumentError
when attempting
to remove the top level node
.
Replaces the current node
in the zipper
with a new node
.
Returns the zipper of the right sibling of the nod
e at this zipper
, or
nil.
Returns the rightmost sibling of the node
at this zipper
, or itself.
Walks the zipper
to the top of the current subtree and returns the that node
.
Searches forward in zipper
for the given pattern, moving to that
pattern or a location inside that pattern.
Returns the zipper
of the right sibling of the node
at this zipper
, or
the next zipper
when no right sibling is available.
Returns a new zipper
that is a subtree of the currently focused node
.
Moves to the top and breaks out of a subtree.
Walks the zipper
to the top of the current subtree and returns that zipper
.
Walks the zipper
to the topmost node, breaking out of any subtrees and returns the top-most zipper
.
Walks the zipper
to the topmost node, breaking out of any subtrees and returns the root node
.
Traverses the tree in depth-first pre-order calling the given fun
for each
node
. When the traversal is finished, the zipper will be back where it
began.
Traverses the tree in depth-first pre-order calling the given fun
for each
node
with an accumulator. When the traversal is finished, the zipper
will be back where it began.
Traverses the tree in depth-first pre-order calling the given fun
for each
node
.
Traverses the tree in depth-first pre-order calling the given fun
for each
node
with an accumulator. When the traversal is finished, the zipper
will be back where it began.
Returns the zipper
for the parent node
of the given zipper
, or nil
if
the zipper
points to the root.
Replaces the current node
in the zipper with the result of applying fun
to
the node
.
Runs the function fun
on the subtree of the currently focused node
and
returns the updated zipper
.
Creates a zipper
from a tree node
.
Types
Functions
Inserts the child
as the rightmost child
of the node
at this zipper
,
without moving.
@spec at(Macro.t(), Sourceror.position()) :: {:ok, t()} | :error
Creates a zipper
from a tree node
focused at the innermost descendant containing position
.
Returns {:ok, zipper}
if position
is within node
, else :error
.
Modifying node
prior to using at/2
is not recommended as added or
changed descendants may not contain accurate position metadata used to
find the focus.
Returns a list of children of the node
. Returns nil
if the node is a leaf.
Returns the zipper
of the leftmost child of the node
at this zipper
, or
nil
if there's no children.
If passed nil
, this function returns nil
.
@spec find(t(), direction :: :prev | :next, predicate :: (tree() -> any())) :: t() | nil
@spec find(nil, direction :: :prev | :next, predicate :: (tree() -> any())) :: nil
Returns a zipper
to the node
that satisfies the predicate
function, or
nil
if none is found.
The optional second parameters specifies the direction
, defaults to
:next
.
If passed nil
, this function returns nil
.
@spec find_all(t(), direction :: :prev | :next, predicate :: (tree() -> any())) :: [ t() ]
@spec find_all(nil, direction :: :prev | :next, predicate :: (tree() -> any())) :: []
Returns a list of zippers
to each node
that satisfies the predicate
function, or
an empty list if none are found.
The optional second parameters specifies the direction
, defaults to
:next
.
If passed nil
, this function returns the empty list.
Like find/3
, but returns the first non-falsy value returned by fun
.
If passed nil
, this function returns nil
.
Inserts the child
as the leftmost child
of the node
at this zipper
,
without moving.
Inserts the child
as the left sibling of the node
at this zipper
,
without moving. Raises an ArgumentError
when attempting to insert a sibling
at the top level.
Inserts the child
as the right sibling of the node
at this zipper
,
without moving. Raises an ArgumentError
when attempting to insert a sibling
at the top level.
Returns the zipper
of the left sibling of the node
at this zipper
, or
nil
.
If passed nil
, this function returns nil
.
Returns the leftmost sibling of the node
at this zipper
, or itself.
If passed nil
, this function returns nil
.
Returns a new branch node
, given an existing node
and new children
.
@spec move_to_cursor(t(), String.t() | t()) :: t() | nil
@spec move_to_cursor(nil, String.t() | t()) :: nil
Matches zipper
against the given pattern, moving to the location of __cursor__()
.
This function only moves zipper
if the current node matches the pattern.
To search for a pattern in zipper
, use search_pattern/2
.
There are two special forms that can be used inside patterns:
__cursor__()
- if the pattern matches, the zipper will be focused at the location of__cursor__()
, if present__
- "wildcard match" that will match a single node of any form.
If passed nil
, this function returns nil
.
Examples
iex> zipper =
...> """
...> if true do
...> 10
...> end
...> """
...> |> Sourceror.parse_string!()
...> |> zip()
iex> pattern =
...> """
...> if __ do
...> __cursor__()
...> end
...> """
iex> found = move_to_cursor(zipper, pattern)
iex> {:__block__, _, [10]} = found.node
Returns the following zipper
in depth-first pre-order.
If passed nil
, this function returns nil
.
Returns the node
at the zipper
.
Returns the previous zipper
in depth-first pre-order. If it's already at
the end, it returns nil
.
Removes the node
at the zipper, returning the zipper
that would have
preceded it in a depth-first walk. Raises an ArgumentError
when attempting
to remove the top level node
.
Replaces the current node
in the zipper
with a new node
.
Returns the zipper of the right sibling of the nod
e at this zipper
, or
nil.
If passed nil
, this function returns nil
.
Returns the rightmost sibling of the node
at this zipper
, or itself.
If passed nil
, this function returns nil
.
Walks the zipper
to the top of the current subtree and returns the that node
.
@spec search_pattern(t(), String.t() | t()) :: t() | nil
@spec search_pattern(nil, String.t() | t()) :: nil
Searches forward in zipper
for the given pattern, moving to that
pattern or a location inside that pattern.
Note that the search may continue outside of zipper
in a depth-first
order. If this isn't desirable, call this function with a subtree/1
.
If passed nil
, this function returns nil
.
There are two special forms that can be used inside patterns:
__cursor__()
- if the pattern matches, the zipper will be focused at the location of__cursor__()
, if present__
- "wildcard match" that will match a single node of any form.
Examples
iex> zipper =
...> """
...> defmodule Example do
...> def my_function(arg1, arg2) do
...> arg1 + arg2
...> end
...> end
...> """
...> |> Sourceror.parse_string!()
...> |> zip()
...> found = search_pattern(zipper, "my_function(arg1, arg2)")
...> {:my_function, _, [{:arg1, _, _}, {:arg2, _, _}]} = found.node
...> found = search_pattern(zipper, "my_function(__, __)")
...> {:my_function, _, [{:arg1, _, _}, {:arg2, _, _}]} = found.node
...> found = search_pattern(zipper, "def my_function(__, __cursor__()), __")
...> {:arg2, _, _} = found.node
@spec skip(t(), direction :: :next | :prev) :: t() | nil
@spec skip(nil, direction :: :next | :prev) :: nil
Returns the zipper
of the right sibling of the node
at this zipper
, or
the next zipper
when no right sibling is available.
This allows to skip subtrees while traversing the siblings of a node.
The optional second parameters specifies the direction
, defaults to
:next
.
If no right/left sibling is available, this function returns the same value as
next/1
/prev/1
.
The function skip/1
behaves like the :skip
in traverse_while/2
and
traverse_while/3
.
If passed nil
, this function returns nil
.
Returns a new zipper
that is a subtree of the currently focused node
.
Moves to the top and breaks out of a subtree.
Returns nil
if zipper
is not a subtree.
Walks the zipper
to the top of the current subtree and returns that zipper
.
Walks the zipper
to the topmost node, breaking out of any subtrees and returns the top-most zipper
.
Walks the zipper
to the topmost node, breaking out of any subtrees and returns the root node
.
Traverses the tree in depth-first pre-order calling the given fun
for each
node
. When the traversal is finished, the zipper will be back where it
began.
If the zipper
is not at the top, just the subtree will be traversed.
The function must return a zipper
.
Traverses the tree in depth-first pre-order calling the given fun
for each
node
with an accumulator. When the traversal is finished, the zipper
will be back where it began.
If the zipper
is not at the top, just the subtree will be traversed.
Traverses the tree in depth-first pre-order calling the given fun
for each
node
.
The traversing will continue if fun
returns {:cont, zipper}
, skipped for
{:skip, zipper}
and halted for {:halt, zipper}
. When the traversal is
finished, the zipper
will be back where it began.
If the zipper
is not at the top, just the subtree will be traversed.
The function must return a zipper
.
@spec traverse_while( t(), term(), (t(), term() -> {:cont, t(), term()} | {:halt, t(), term()} | {:skip, t(), term()}) ) :: {t(), term()}
Traverses the tree in depth-first pre-order calling the given fun
for each
node
with an accumulator. When the traversal is finished, the zipper
will be back where it began.
The traversing will continue if fun
returns {:cont, zipper, acc}
, skipped
for {:skip, zipper, acc}
and halted for {:halt, zipper, acc}
If the zipper
is not at the top, just the subtree will be traversed.
Returns the zipper
for the parent node
of the given zipper
, or nil
if
the zipper
points to the root.
Replaces the current node
in the zipper with the result of applying fun
to
the node
.
Runs the function fun
on the subtree of the currently focused node
and
returns the updated zipper
.
fun
must return a zipper, which may be positioned at the top of the subtree.
Creates a zipper
from a tree node
.