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:

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 node 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

@opaque path()
@type t() :: %Sourceror.Zipper{node: tree(), path: path() | nil, supertree: t() | nil}
@type tree() :: Macro.t()

Functions

Link to this function

append_child(zipper, child)

View Source

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.

@spec branch?(tree()) :: boolean()
@spec children(tree()) :: [tree()] | nil

Returns a list of children of the node. Returns nil if the node is a leaf.

@spec down(t()) :: t() | nil
@spec down(nil) :: nil

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.

Link to this function

find(zipper, direction \\ :next, predicate)

View Source
@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.

Link to this function

find_all(zipper, direction \\ :next, predicate)

View Source
@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.

Link to this function

find_value(zipper, direction \\ :next, fun)

View Source

Like find/3, but returns the first non-falsy value returned by fun.

If passed nil, this function returns nil.

Link to this function

insert_child(zipper, child)

View Source

Inserts the child as the leftmost child of the node at this zipper, without moving.

Link to this function

insert_left(zipper, child)

View Source
@spec insert_left(t(), tree()) :: t()

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.

Link to this function

insert_right(zipper, child)

View Source
@spec insert_right(t(), tree()) :: t()

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.

@spec left(t()) :: t() | nil
@spec left(nil) :: nil

Returns the zipper of the left sibling of the node at this zipper, or nil.

If passed nil, this function returns nil.

@spec leftmost(t()) :: t()
@spec leftmost(nil) :: nil

Returns the leftmost sibling of the node at this zipper, or itself.

If passed nil, this function returns nil.

Link to this function

make_node(node, children)

View Source
@spec make_node(tree(), [tree()]) :: tree()

Returns a new branch node, given an existing node and new children.

Link to this function

move_to_cursor(zipper, pattern)

View Source
@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
@spec next(t()) :: t() | nil
@spec next(nil) :: nil

Returns the following zipper in depth-first pre-order.

If passed nil, this function returns nil.

@spec node(t()) :: tree()

Returns the node at the zipper.

@spec prev(t()) :: t()

Returns the previous zipper in depth-first pre-order. If it's already at the end, it returns nil.

@spec remove(t()) :: t()

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.

@spec replace(t(), tree()) :: t()

Replaces the current node in the zipper with a new node.

@spec right(t()) :: t() | nil
@spec right(nil) :: nil

Returns the zipper of the right sibling of the node at this zipper, or nil.

If passed nil, this function returns nil.

@spec rightmost(t()) :: t()
@spec rightmost(nil) :: nil

Returns the rightmost sibling of the node at this zipper, or itself.

If passed nil, this function returns nil.

@spec root(t()) :: tree()

Walks the zipper to the top of the current subtree and returns the that node.

Link to this function

search_pattern(zipper, pattern)

View Source
@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
Link to this function

skip(zipper, direction \\ :next)

View Source
@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.

@spec subtree(t()) :: t()

Returns a new zipper that is a subtree of the currently focused node.

@spec supertree(t()) :: t() | nil

Moves to the top and breaks out of a subtree.

Returns nil if zipper is not a subtree.

@spec top(t()) :: t()

Walks the zipper to the top of the current subtree and returns that zipper.

@spec topmost(t()) :: t()

Walks the zipper to the topmost node, breaking out of any subtrees and returns the top-most zipper.

@spec topmost_root(t()) :: tree()

Walks the zipper to the topmost node, breaking out of any subtrees and returns the root node.

@spec traverse(t(), (t() -> t())) :: t()

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.

Link to this function

traverse(zipper, acc, fun)

View Source
@spec traverse(t(), term(), (t(), term() -> {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.

If the zipper is not at the top, just the subtree will be traversed.

Link to this function

traverse_while(zipper, fun)

View Source
@spec traverse_while(t(), (t() -> {:cont, t()} | {:halt, t()} | {:skip, t()})) :: t()

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.

Link to this function

traverse_while(zipper, acc, fun)

View Source
@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.

@spec up(t()) :: t() | nil

Returns the zipper for the parent node of the given zipper, or nil if the zipper points to the root.

@spec update(t(), (tree() -> tree())) :: t()

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.

@spec zip(tree()) :: t()

Creates a zipper from a tree node.