-module(greenwood).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/greenwood.gleam").
-export([node/2, node_with_trivia/3, node_element/2, node_element_with_trivia/3, token/2, token_element/2, implicit_token/1, implicit_token_element/1, fold/3, fold_with_depth/3, each_with_depth/2, each/2, visitor/0, on_token/2, on_enter_node/2, on_exit_node/2, on_trivia/2, trailing_trivia/1, leading_trivia/1, traverse/3, map_children/2, map_tree_up/2, map_tree_down/2, find_child/2, filter_children/2, find_descendant/2, replace_child/3, replace_first/3, insert_before/3, insert_after/3, remove_children/2, append_child/2, prepend_child/2, set_leading_trivia/2, set_trailing_trivia/2, all_trivia/1, zip/1, down/1, down_where/2, left/1, left_where/2, right/1, right_where/2, up/1, up_n/2, left_n/2, left_n_where/3, right_n/2, right_n_where/3, set_focus/2, map_focus/2, unzip/1]).
-export_type([token/1, trivia/1, node_/1, element/1, zipper/1, crumb/1, traverse_action/1, visitor/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Greenwood: a generic trivia-preserving concrete syntax tree\n"
"\n"
" Greenwood provides an immutable concrete syntax tree parameterized by node\n"
" and token `kind`, with associated trivia and structural transformation\n"
" primitives. Greenwood syntax trees are format-agnostic and parsers supply\n"
" their own `kind` types.\n"
"\n"
" ### Function Groups\n"
"\n"
" There are six types of functions in the Greenwood interface:\n"
"\n"
" - `builder`: build a Greenwood tree\n"
" - `query`: interrogate a Greenwood tree\n"
" - `transformer`: transform a Greenwood tree\n"
" - `traversal`: traverse a Greenwood tree\n"
" - `trivia`: manage trivia on Greenwood tree node(s)\n"
" - `cursor`: navigate and edit a Greenwood tree with a movable cursor,\n"
" provided by `greenwood/zipper`\n"
"\n"
" Each function and type in the Greenwood interface is marked with the\n"
" appropriate interface group or groups.\n"
"\n"
" ### `Node`s and `Token`s\n"
"\n"
" Greenwood distinguishes between `Node` elements (which may carry child\n"
" nodes or tokens) and `Token` elements (which carry text). Both may carry\n"
" meaning, but `Token` elements are used to represent leaf elements and the\n"
" text associated with them.\n"
"\n"
" ### Trivia\n"
"\n"
" In syntax tree terminology, \"trivia\" refers to source text that has no\n"
" semantic meaning to the language but matters to humans: whitespace,\n"
" comments, blank lines, and sometimes preprocessor directives. A pure AST\n"
" discards trivia entirely, but a CST must track it.\n"
"\n"
" Greenwood implements a hybrid concrete syntax tree supporting\n"
" [Roslyn][dn]-style Trivia annotations (attached trivia) or\n"
" [Rowan][rust]-style green tree child tokens (inline trivia), depending on\n"
" the choices made by the parser.\n"
"\n"
" - Attached trivia (Roslyn-style) is where each node carries leading and\n"
" trailing trivia tokens. A comment above a function \"belongs to\" that\n"
" function node. This makes it easy to move a node and have its comments\n"
" follow. Greenwood supports this with `Trivia(leading, trailing)`.\n"
"\n"
" - Inline trivia (Rowan-style) tokens are siblings in the children list,\n"
" with no special attachment. The tree is uniform but the parser (or\n"
" a later pass) must decide ownership when moving nodes. Greenwood supports\n"
" this with `Bare` trivia markers.\n"
"\n"
" ### Concrete and Abstract Syntax Trees\n"
"\n"
" A concrete syntax tree is a tree representation of the actual tokens for\n"
" a language, whereas abstract syntax trees are simplified forms discarding\n"
" anything not necessary for resolution of the language into meaning.\n"
"\n"
" As an example, consider what the abstract syntax tree for the following\n"
" Gleam code might look like:\n"
"\n"
" ```gleam\n"
" /// Tail-recursive Fibonacci sequence implementation\n"
" pub fn fib(n: Int) -> Int {\n"
" case n {\n"
" 0 | 1 -> n\n"
" _ -> fib(n - 1) + fib(n - 2)\n"
" }\n"
" }\n"
" ```\n"
"\n"
" This would produce an executable abstract syntax tree like:\n"
"\n"
" ```gleam\n"
" Function(\n"
" name: \"fib\", publicity: Public,\n"
" parameters: [Parameter(name: \"n\", type: Int)],\n"
" return_type: Int,\n"
" body: Case(\n"
" subjects: [Variable(\"n\")],\n"
" clauses: [\n"
" Clause(patterns: [Int(0), Int(1)], body: Variable(\"n\")),\n"
" Clause(patterns: [Discard], body: BinOp(\n"
" Add,\n"
" Call(\"fib\", [BinOp(Sub, Variable(\"n\"), Int(1))]),\n"
" Call(\"fib\", [BinOp(Sub, Variable(\"n\"), Int(2))]),\n"
" )),\n"
" ],\n"
" ),\n"
" )\n"
" ```\n"
"\n"
" This could be transformed, but when rendered back to Gleam, the result\n"
" would _drop_ the leading documentation comment. Comparatively, the concrete\n"
" syntax tree is about the form of the text and _keeps_ the leading\n"
" documentation comment. Every single run of contiguous whitespace is\n"
" accounted for. Everything is assigned a meaning for preservation.\n"
"\n"
" ```gleam\n"
" Node(\n"
" kind: Function,\n"
" trivia: Trivia(leading: [\n"
" Token(\n"
" DocComment,\n"
" \"/// Tail-recursive Fibonacci sequence implementation\",\n"
" ),\n"
" Token(Newline, \"\\n\"),\n"
" ]),\n"
" children: [\n"
" Token(Pub, \"pub\"),\n"
" Token(Whitespace, \" \"),\n"
" Token(Fn, \"fn\"),\n"
" Token(Whitespace, \" \"),\n"
" Token(Name, \"fib\"),\n"
" Token(LeftParen, \"(\"),\n"
" Token(Name, \"n\"),\n"
" Token(Colon, \":\"),\n"
" Token(Whitespace, \" \"),\n"
" Token(UpperName, \"Int\"),\n"
" Token(RightParen, \")\"),\n"
" Token(Whitespace, \" \"),\n"
" Token(Arrow, \"->\"),\n"
" Token(Whitespace, \" \"),\n"
" Token(UpperName, \"Int\"),\n"
" Token(Whitespace, \" \"),\n"
" Token(LeftBrace, \"{\"),\n"
" Token(Newline, \"\\n\"),\n"
" Token(Whitespace, \" \"),\n"
" Node(Case, [\n"
" Token(Case, \"case\"),\n"
" Token(Whitespace, \" \"),\n"
" Token(Name, \"n\"),\n"
" Token(Whitespace, \" \"),\n"
" Token(LeftBrace, \"{\"),\n"
" Token(Newline, \"\\n\"),\n"
" ...every token including whitespace, pipes, arrows...\n"
" ], Bare),\n"
" Token(Newline, \"\\n\"),\n"
" Token(RightBrace, \"}\"),\n"
" Token(Newline, \"\\n\"),\n"
" ],\n"
" )\n"
" ```\n"
"\n"
" The differences are vast: the abstract syntax tree tells you what the code\n"
" means, but the concrete syntax tree tells you what the source looks like.\n"
" The source can be reconstructed byte-for-byte from the concrete syntax\n"
" tree.\n"
"\n"
" This makes a concrete syntax tree useful for editors, language server\n"
" implementations, and for edit-safe transformations.\n"
"\n"
" > For sufficiently distinct nodes, it is possible to design the syntax tree\n"
" > to be somewhere between a concrete tree and an abstract tree. As an\n"
" > example, `LeftBrace` nodes always have only the text `\"{\"`, so the\n"
" > representation could be `Token(LeftBrace, \"\")`.\n"
" >\n"
" > Greenwood v2 will support this use case explicitly with a new `Token`\n"
" > variant, `ImplicitToken`. The current version provides constructor\n"
" > functions to assist with this migration: `implicit_token` and\n"
" > `implicit_token_element`.\n"
"\n"
" [dn]: https://github.com/dotnet/roslyn/blob/main/docs/wiki/Roslyn-Overview.md#syntax-trivia\n"
" [rust]: https://github.com/rust-analyzer/rowan\n"
).
-type token(DKT) :: {token, DKT, binary()}.
-type trivia(DKU) :: {trivia, list(token(DKU)), list(token(DKU))} | bare.
-type node_(DKV) :: {node, DKV, list(element(DKV)), trivia(DKV)}.
-type element(DKW) :: {node_element, node_(DKW)} | {token_element, token(DKW)}.
-type zipper(DKX) :: {zipper, node_(DKX), list(crumb(DKX))}.
-type crumb(DKY) :: {crumb,
DKY,
trivia(DKY),
list(element(DKY)),
list(element(DKY))}.
-type traverse_action(DKZ) :: {continue, DKZ} | {skip, DKZ} | {stop, DKZ}.
-type visitor(DLA, DLB) :: {visitor,
gleam@option:option(fun((DLB, token(DLA)) -> traverse_action(DLB))),
gleam@option:option(fun((DLB, node_(DLA)) -> traverse_action(DLB))),
gleam@option:option(fun((DLB, node_(DLA)) -> traverse_action(DLB))),
gleam@option:option(fun((DLB, token(DLA)) -> traverse_action(DLB)))}.
-file("src/greenwood.gleam", 319).
?DOC(
" Create a node without Trivia.\n"
"\n"
" This is the same as `Node(kind:, children:, trivia: Bare)`.\n"
"\n"
" `builder`\n"
).
-spec node(DLC, list(element(DLC))) -> node_(DLC).
node(Kind, Children) ->
{node, Kind, Children, bare}.
-file("src/greenwood.gleam", 331).
?DOC(
" Create a node with Trivia.\n"
"\n"
" This is the same as `Node(kind:, children:, trivia:)`.\n"
"\n"
" `builder`\n"
).
-spec node_with_trivia(DLG, list(element(DLG)), trivia(DLG)) -> node_(DLG).
node_with_trivia(Kind, Children, Trivia) ->
{node, Kind, Children, Trivia}.
-file("src/greenwood.gleam", 343).
?DOC(
" Create a node element without Trivia. This is the same as\n"
" `NodeElement(Node(kind:, children:, trivia: Bare))`.\n"
"\n"
" `builder`\n"
).
-spec node_element(DLL, list(element(DLL))) -> element(DLL).
node_element(Kind, Children) ->
{node_element, {node, Kind, Children, bare}}.
-file("src/greenwood.gleam", 354).
?DOC(
" Create a node element with Trivia. This is the same as\n"
" `NodeElement(Node(kind:, children:, trivia:))`.\n"
"\n"
" `builder`\n"
).
-spec node_element_with_trivia(DLP, list(element(DLP)), trivia(DLP)) -> element(DLP).
node_element_with_trivia(Kind, Children, Trivia) ->
{node_element, {node, Kind, Children, Trivia}}.
-file("src/greenwood.gleam", 365).
?DOC(
" Create a token. This is the same as `Token(kind:, text:)`.\n"
"\n"
" `builder`\n"
).
-spec token(DLU, binary()) -> token(DLU).
token(Kind, Text) ->
{token, Kind, Text}.
-file("src/greenwood.gleam", 373).
?DOC(
" Create a token element. This is the same as `TokenElement(Token(kind:,\n"
" text:))`.\n"
"\n"
" `builder`\n"
).
-spec token_element(DLW, binary()) -> element(DLW).
token_element(Kind, Text) ->
{token_element, {token, Kind, Text}}.
-file("src/greenwood.gleam", 383).
?DOC(
" Create an implicit token.\n"
"\n"
" In greenwood v1, this is the same as `Token(kind:, text: \"\")`. In greenwood\n"
" v2, it will be `ImplicitToken(kind:)`.\n"
"\n"
" `builder`\n"
).
-spec implicit_token(DLY) -> token(DLY).
implicit_token(Kind) ->
{token, Kind, <<""/utf8>>}.
-file("src/greenwood.gleam", 393).
?DOC(
" Create an implicit token element.\n"
"\n"
" In greenwood v1, this is the same as `TokenElement(Token(kind:, text: \"\"))`.\n"
" In greenwood v2, it will be `TokenElement(ImplicitToken(kind:))`.\n"
"\n"
" `builder`\n"
).
-spec implicit_token_element(DMA) -> element(DMA).
implicit_token_element(Kind) ->
{token_element, {token, Kind, <<""/utf8>>}}.
-file("src/greenwood.gleam", 1239).
-spec do_fold(list(element(DYS)), DYV, fun((DYV, element(DYS)) -> DYV)) -> DYV.
do_fold(Children, Acc, F) ->
gleam@list:fold(
Children,
Acc,
fun(Acc@1, El) ->
Acc@2 = F(Acc@1, El),
case El of
{node_element, Child} ->
do_fold(erlang:element(3, Child), Acc@2, F);
{token_element, _} ->
Acc@2
end
end
).
-file("src/greenwood.gleam", 403).
?DOC(
" Recursively fold over all elements depth-first.\n"
"\n"
" If you only need to fold over a node's immediate children, consider using\n"
" `node.children |> list.fold(...)` directly.\n"
"\n"
" `traversal`\n"
).
-spec fold(node_(DMC), DME, fun((DME, element(DMC)) -> DME)) -> DME.
fold(Node, Acc, F) ->
do_fold(erlang:element(3, Node), Acc, F).
-file("src/greenwood.gleam", 1253).
-spec do_fold_with_depth(
list(element(DYX)),
DZA,
fun((DZA, element(DYX), integer()) -> DZA),
integer()
) -> DZA.
do_fold_with_depth(Children, Acc, F, Depth) ->
gleam@list:fold(
Children,
Acc,
fun(Acc@1, El) ->
Acc@2 = F(Acc@1, El, Depth),
case El of
{node_element, Child} ->
do_fold_with_depth(
erlang:element(3, Child),
Acc@2,
F,
Depth + 1
);
{token_element, _} ->
Acc@2
end
end
).
-file("src/greenwood.gleam", 415).
?DOC(
" Recursively fold over all elements depth-first, with depth. Depth `0` is the\n"
" immediate children of the provided `node` (e.g., `node.children`).\n"
"\n"
" `traversal`\n"
).
-spec fold_with_depth(
node_(DMG),
DMI,
fun((DMI, element(DMG), integer()) -> DMI)
) -> DMI.
fold_with_depth(Node, Acc, F) ->
do_fold_with_depth(erlang:element(3, Node), Acc, F, 0).
-file("src/greenwood.gleam", 1269).
-spec do_each_with_depth(
list(element(DZC)),
fun((element(DZC), integer()) -> nil),
integer()
) -> nil.
do_each_with_depth(Children, F, Depth) ->
gleam@list:each(
Children,
fun(El) ->
F(El, Depth),
case El of
{node_element, Child} ->
do_each_with_depth(erlang:element(3, Child), F, Depth + 1);
{token_element, _} ->
nil
end
end
).
-file("src/greenwood.gleam", 426).
?DOC(
" Recursively traverse all elements for side effects, with depth.\n"
"\n"
" `traversal`\n"
).
-spec each_with_depth(node_(DMK), fun((element(DMK), integer()) -> nil)) -> nil.
each_with_depth(Node, F) ->
do_each_with_depth(erlang:element(3, Node), F, 0).
-file("src/greenwood.gleam", 1283).
-spec do_each(list(element(DZG)), fun((element(DZG)) -> nil)) -> nil.
do_each(Children, F) ->
gleam@list:each(
Children,
fun(El) ->
F(El),
case El of
{node_element, Child} ->
do_each(erlang:element(3, Child), F);
{token_element, _} ->
nil
end
end
).
-file("src/greenwood.gleam", 436).
?DOC(
" Recursively traverse all elements for side effects.\n"
"\n"
" `traversal`\n"
).
-spec each(node_(DMN), fun((element(DMN)) -> nil)) -> nil.
each(Node, F) ->
do_each(erlang:element(3, Node), F).
-file("src/greenwood.gleam", 443).
?DOC(
" Create a visitor with all callbacks set to `None` (no-op, always continues).\n"
"\n"
" `traversal`\n"
).
-spec visitor() -> visitor(any(), any()).
visitor() ->
{visitor, none, none, none, none}.
-file("src/greenwood.gleam", 450).
?DOC(
" Set the token callback on a visitor.\n"
"\n"
" `traversal`\n"
).
-spec on_token(
visitor(DMU, DMV),
fun((DMV, token(DMU)) -> traverse_action(DMV))
) -> visitor(DMU, DMV).
on_token(V, F) ->
{visitor,
{some, F},
erlang:element(3, V),
erlang:element(4, V),
erlang:element(5, V)}.
-file("src/greenwood.gleam", 460).
?DOC(
" Set the enter_node callback on a visitor.\n"
"\n"
" `traversal`\n"
).
-spec on_enter_node(
visitor(DNC, DND),
fun((DND, node_(DNC)) -> traverse_action(DND))
) -> visitor(DNC, DND).
on_enter_node(V, F) ->
{visitor,
erlang:element(2, V),
{some, F},
erlang:element(4, V),
erlang:element(5, V)}.
-file("src/greenwood.gleam", 470).
?DOC(
" Set the exit_node callback on a visitor.\n"
"\n"
" `traversal`\n"
).
-spec on_exit_node(
visitor(DNK, DNL),
fun((DNL, node_(DNK)) -> traverse_action(DNL))
) -> visitor(DNK, DNL).
on_exit_node(V, F) ->
{visitor,
erlang:element(2, V),
erlang:element(3, V),
{some, F},
erlang:element(5, V)}.
-file("src/greenwood.gleam", 480).
?DOC(
" Set the trivia callback on a visitor.\n"
"\n"
" `traversal`\n"
).
-spec on_trivia(
visitor(DNS, DNT),
fun((DNT, token(DNS)) -> traverse_action(DNT))
) -> visitor(DNS, DNT).
on_trivia(V, F) ->
{visitor,
erlang:element(2, V),
erlang:element(3, V),
erlang:element(4, V),
{some, F}}.
-file("src/greenwood.gleam", 746).
?DOC(
" Get trailing trivia tokens from a node.\n"
"\n"
" `trivia`\n"
).
-spec trailing_trivia(node_(DRH)) -> list(token(DRH)).
trailing_trivia(Node) ->
case erlang:element(4, Node) of
{trivia, _, Trailing} ->
Trailing;
bare ->
[]
end.
-file("src/greenwood.gleam", 1382).
-spec do_traverse_trivia(
list(token(EAM)),
EAP,
fun((EAP, token(EAM)) -> traverse_action(EAP))
) -> traverse_action(EAP).
do_traverse_trivia(Trivia, Acc, F) ->
case Trivia of
[] ->
{continue, Acc};
[Tok | Rest] ->
case F(Acc, Tok) of
{stop, _} = Stop ->
Stop;
{continue, Acc@1} ->
do_traverse_trivia(Rest, Acc@1, F);
{skip, Acc@1} ->
do_traverse_trivia(Rest, Acc@1, F)
end
end.
-file("src/greenwood.gleam", 1347).
-spec traverse_trivia(
list(token(DZX)),
visitor(DZX, EAA),
EAA,
fun((EAA) -> traverse_action(EAA))
) -> traverse_action(EAA).
traverse_trivia(Trivia, Visitor, Acc, Callback) ->
Result = case erlang:element(5, Visitor) of
none ->
{continue, Acc};
{some, F} ->
do_traverse_trivia(Trivia, Acc, F)
end,
case Result of
{stop, _} = Stop ->
Stop;
{continue, Acc@1} ->
Callback(Acc@1);
{skip, Acc@1} ->
Callback(Acc@1)
end.
-file("src/greenwood.gleam", 1364).
-spec enter_node(
node_(EAF),
visitor(EAF, EAH),
EAH,
fun((EAH) -> traverse_action(EAH))
) -> traverse_action(EAH).
enter_node(Node, Visitor, Acc, Callback) ->
Result = case erlang:element(3, Visitor) of
none ->
{continue, Acc};
{some, F} ->
F(Acc, Node)
end,
case Result of
{stop, _} = Stop ->
Stop;
{skip, Acc@1} ->
{continue, Acc@1};
{continue, Acc@2} ->
Callback(Acc@2)
end.
-file("src/greenwood.gleam", 736).
?DOC(
" Get leading trivia tokens from a node.\n"
"\n"
" `trivia`\n"
).
-spec leading_trivia(node_(DRD)) -> list(token(DRD)).
leading_trivia(Node) ->
case erlang:element(4, Node) of
{trivia, Leading, _} ->
Leading;
bare ->
[]
end.
-file("src/greenwood.gleam", 1321).
-spec do_traverse_children(list(element(DZQ)), DZT, visitor(DZQ, DZT)) -> traverse_action(DZT).
do_traverse_children(Children, Acc, Visitor) ->
case Children of
[] ->
{continue, Acc};
[El | Rest] ->
Result = case El of
{token_element, Tok} ->
case erlang:element(2, Visitor) of
none ->
{continue, Acc};
{some, F} ->
F(Acc, Tok)
end;
{node_element, Child} ->
do_traverse_node(Child, Acc, Visitor)
end,
case Result of
{stop, _} = Stop ->
Stop;
{continue, Acc@1} ->
do_traverse_children(Rest, Acc@1, Visitor);
{skip, Acc@1} ->
do_traverse_children(Rest, Acc@1, Visitor)
end
end.
-file("src/greenwood.gleam", 1293).
-spec do_traverse_node(node_(DZK), DZM, visitor(DZK, DZM)) -> traverse_action(DZM).
do_traverse_node(Node, Acc, Visitor) ->
traverse_trivia(
leading_trivia(Node),
Visitor,
Acc,
fun(Acc@1) ->
enter_node(
Node,
Visitor,
Acc@1,
fun(Acc@2) ->
case do_traverse_children(
erlang:element(3, Node),
Acc@2,
Visitor
) of
{stop, _} = Stop ->
Stop;
{continue, Acc@3} ->
traverse_trivia(
trailing_trivia(Node),
Visitor,
Acc@3,
fun(Acc@4) -> case erlang:element(4, Visitor) of
none ->
{continue, Acc@4};
{some, F} ->
F(Acc@4, Node)
end end
);
{skip, Acc@3} ->
traverse_trivia(
trailing_trivia(Node),
Visitor,
Acc@3,
fun(Acc@4) -> case erlang:element(4, Visitor) of
none ->
{continue, Acc@4};
{some, F} ->
F(Acc@4, Node)
end end
)
end
end
)
end
).
-file("src/greenwood.gleam", 503).
?DOC(
" Traverse a tree with a visitor, folding an accumulator through callbacks.\n"
"\n"
" Visits the full tree including the root node in the following traversal\n"
" order:\n"
"\n"
" 1. Leading trivia (if `trivia` callback set)\n"
" 2. `enter_node`\n"
" 3. Children (tokens invoke `token`; nodes recurse)\n"
" 4. Trailing trivia (if `trivia` callback set)\n"
" 5. `exit_node`\n"
"\n"
" `Stop` from any callback halts immediately. `Skip` from `enter_node` skips\n"
" children and exit for that node. In all other cases `Skip` behaves like\n"
" `Continue`.\n"
"\n"
" `traversal`\n"
).
-spec traverse(node_(DOA), DOC, visitor(DOA, DOC)) -> DOC.
traverse(Node, Acc, Visitor) ->
case do_traverse_node(Node, Acc, Visitor) of
{continue, A} ->
A;
{skip, A@1} ->
A@1;
{stop, A@2} ->
A@2
end.
-file("src/greenwood.gleam", 518).
?DOC(
" Map over immediate children of a node.\n"
"\n"
" `transformer`\n"
).
-spec map_children(node_(DOF), fun((element(DOF)) -> element(DOF))) -> node_(DOF).
map_children(Node, F) ->
{node,
erlang:element(2, Node),
gleam@list:map(erlang:element(3, Node), F),
erlang:element(4, Node)}.
-file("src/greenwood.gleam", 537).
?DOC(
" Recursively map all elements leaf nodes first. Child nodes are processed\n"
" before sibling nodes.\n"
"\n"
" ```\n"
" A ← processed last\n"
" / \\\n"
" B C ← processed second\n"
" / \\\n"
" D E ← processed first\n"
" ```\n"
"\n"
" `transformer`\n"
).
-spec map_tree_up(node_(DOK), fun((element(DOK)) -> element(DOK))) -> node_(DOK).
map_tree_up(Node, F) ->
Children = gleam@list:map(erlang:element(3, Node), fun(El) -> case El of
{node_element, Child} ->
F({node_element, map_tree_up(Child, F)});
{token_element, _} ->
F(El)
end end),
{node, erlang:element(2, Node), Children, erlang:element(4, Node)}.
-file("src/greenwood.gleam", 567).
?DOC(
" Recursively map all elements root node first. Child nodes are processed\n"
" before sibling nodes.\n"
"\n"
" Unlike `map_tree_up`, this runs the mapping function over the root node\n"
" first and recurses into the _result_ of the mapping function. It is\n"
" therefore possible to affect the next iteration with the mapping function.\n"
"\n"
" ```\n"
" A ← processed first\n"
" / \\\n"
" B′ C′ ← processed second\n"
" / \\\n"
" D″ E″ ← processed last\n"
" ```\n"
"\n"
" `transformer`\n"
).
-spec map_tree_down(node_(DOP), fun((element(DOP)) -> element(DOP))) -> node_(DOP).
map_tree_down(Node, F) ->
Children = gleam@list:map(
erlang:element(3, Node),
fun(El) ->
Mapped = F(El),
case Mapped of
{node_element, Child} ->
{node_element, map_tree_down(Child, F)};
{token_element, _} ->
Mapped
end
end
),
{node, erlang:element(2, Node), Children, erlang:element(4, Node)}.
-file("src/greenwood.gleam", 585).
?DOC(
" Find the first immediate child element matching a predicate.\n"
"\n"
" `query`\n"
).
-spec find_child(node_(DOU), fun((element(DOU)) -> boolean())) -> gleam@option:option(element(DOU)).
find_child(Node, Predicate) ->
_pipe = gleam@list:find(erlang:element(3, Node), Predicate),
gleam@option:from_result(_pipe).
-file("src/greenwood.gleam", 595).
?DOC(
" Find all immediate child elements matching a predicate.\n"
"\n"
" `query`\n"
).
-spec filter_children(node_(DOZ), fun((element(DOZ)) -> boolean())) -> list(element(DOZ)).
filter_children(Node, Predicate) ->
gleam@list:filter(erlang:element(3, Node), Predicate).
-file("src/greenwood.gleam", 961).
-spec do_find_descendant(list(element(DUB)), fun((element(DUB)) -> boolean())) -> gleam@option:option(element(DUB)).
do_find_descendant(Elements, Predicate) ->
case Elements of
[] ->
none;
[El | Rest] ->
gleam@bool:guard(Predicate(El), {some, El}, fun() -> case El of
{node_element, Child} ->
_pipe = do_find_descendant(
erlang:element(3, Child),
Predicate
),
gleam@option:lazy_or(
_pipe,
fun() -> do_find_descendant(Rest, Predicate) end
);
{token_element, _} ->
do_find_descendant(Rest, Predicate)
end end)
end.
-file("src/greenwood.gleam", 606).
?DOC(
" Find the first descendant matching a predicate. Searches from root to leaves\n"
" returning the shallowest match.\n"
"\n"
" `query`\n"
).
-spec find_descendant(node_(DPE), fun((element(DPE)) -> boolean())) -> gleam@option:option(element(DPE)).
find_descendant(Node, Predicate) ->
do_find_descendant(erlang:element(3, Node), Predicate).
-file("src/greenwood.gleam", 616).
?DOC(
" Replace a child at a given index.\n"
"\n"
" `transformer`\n"
).
-spec replace_child(node_(DPJ), integer(), element(DPJ)) -> node_(DPJ).
replace_child(Node, Index, Element) ->
Children = gleam@list:index_map(
erlang:element(3, Node),
fun(El, I) ->
gleam@bool:guard(I =:= Index, Element, fun() -> El end)
end
),
{node, erlang:element(2, Node), Children, erlang:element(4, Node)}.
-file("src/greenwood.gleam", 980).
-spec do_replace_first(
list(element(DUH)),
fun((element(DUH)) -> boolean()),
fun((element(DUH)) -> element(DUH)),
list(element(DUH))
) -> list(element(DUH)).
do_replace_first(Elements, Predicate, Replacement, Acc) ->
case Elements of
[] ->
lists:reverse(Acc);
[El | Rest] ->
gleam@bool:guard(
Predicate(El),
lists:append(lists:reverse(Acc), [Replacement(El) | Rest]),
fun() ->
do_replace_first(Rest, Predicate, Replacement, [El | Acc])
end
)
end.
-file("src/greenwood.gleam", 632).
?DOC(
" Replace the first child matching a predicate.\n"
"\n"
" `transformer`\n"
).
-spec replace_first(
node_(DPN),
fun((element(DPN)) -> boolean()),
fun((element(DPN)) -> element(DPN))
) -> node_(DPN).
replace_first(Node, Predicate, Replacement) ->
Children = do_replace_first(
erlang:element(3, Node),
Predicate,
Replacement,
[]
),
{node, erlang:element(2, Node), Children, erlang:element(4, Node)}.
-file("src/greenwood.gleam", 998).
-spec do_insert_before(
list(element(DUR)),
fun((element(DUR)) -> boolean()),
element(DUR),
list(element(DUR))
) -> list(element(DUR)).
do_insert_before(Elements, Predicate, Element, Acc) ->
case Elements of
[] ->
lists:reverse(Acc);
[El | Rest] ->
gleam@bool:guard(
Predicate(El),
lists:append(lists:reverse(Acc), [Element, El | Rest]),
fun() ->
do_insert_before(Rest, Predicate, Element, [El | Acc])
end
)
end.
-file("src/greenwood.gleam", 644).
?DOC(
" Insert an element before the first child matching a predicate.\n"
"\n"
" `transformer`\n"
).
-spec insert_before(node_(DPT), fun((element(DPT)) -> boolean()), element(DPT)) -> node_(DPT).
insert_before(Node, Predicate, Element) ->
Children = do_insert_before(erlang:element(3, Node), Predicate, Element, []),
{node, erlang:element(2, Node), Children, erlang:element(4, Node)}.
-file("src/greenwood.gleam", 1016).
-spec do_insert_after(
list(element(DVA)),
fun((element(DVA)) -> boolean()),
element(DVA),
list(element(DVA))
) -> list(element(DVA)).
do_insert_after(Elements, Predicate, Element, Acc) ->
case Elements of
[] ->
lists:reverse(Acc);
[El | Rest] ->
gleam@bool:guard(
Predicate(El),
lists:append(lists:reverse(Acc), [El, Element | Rest]),
fun() ->
do_insert_after(Rest, Predicate, Element, [El | Acc])
end
)
end.
-file("src/greenwood.gleam", 656).
?DOC(
" Insert an element after the first child matching a predicate.\n"
"\n"
" `transformer`\n"
).
-spec insert_after(node_(DPY), fun((element(DPY)) -> boolean()), element(DPY)) -> node_(DPY).
insert_after(Node, Predicate, Element) ->
Children = do_insert_after(erlang:element(3, Node), Predicate, Element, []),
{node, erlang:element(2, Node), Children, erlang:element(4, Node)}.
-file("src/greenwood.gleam", 668).
?DOC(
" Remove all children matching a predicate.\n"
"\n"
" `transformer`\n"
).
-spec remove_children(node_(DQD), fun((element(DQD)) -> boolean())) -> node_(DQD).
remove_children(Node, Predicate) ->
{node,
erlang:element(2, Node),
gleam@list:filter(
erlang:element(3, Node),
fun(El) -> not Predicate(El) end
),
erlang:element(4, Node)}.
-file("src/greenwood.gleam", 678).
?DOC(
" Append a child to the end of a node's children.\n"
"\n"
" `transformer`\n"
).
-spec append_child(node_(DQH), element(DQH)) -> node_(DQH).
append_child(Node, Element) ->
{node,
erlang:element(2, Node),
lists:append(erlang:element(3, Node), [Element]),
erlang:element(4, Node)}.
-file("src/greenwood.gleam", 688).
?DOC(
" Prepend a child to the beginning of a node's children.\n"
"\n"
" `transformer`\n"
).
-spec prepend_child(node_(DQL), element(DQL)) -> node_(DQL).
prepend_child(Node, Element) ->
{node,
erlang:element(2, Node),
[Element | erlang:element(3, Node)],
erlang:element(4, Node)}.
-file("src/greenwood.gleam", 698).
?DOC(
" Attach leading trivia to a node.\n"
"\n"
" `trivia`\n"
).
-spec set_leading_trivia(node_(DQP), list(token(DQP))) -> node_(DQP).
set_leading_trivia(Node, Tokens) ->
New_trivia = case erlang:element(4, Node) of
{trivia, _, T} ->
{trivia, Tokens, T};
bare ->
{trivia, Tokens, []}
end,
{node, erlang:element(2, Node), erlang:element(3, Node), New_trivia}.
-file("src/greenwood.gleam", 712).
?DOC(
" Attach trailing trivia to a node.\n"
"\n"
" `trivia`\n"
).
-spec set_trailing_trivia(node_(DQU), list(token(DQU))) -> node_(DQU).
set_trailing_trivia(Node, Tokens) ->
New_trivia = case erlang:element(4, Node) of
{trivia, L, _} ->
{trivia, L, Tokens};
bare ->
{trivia, [], Tokens}
end,
{node, erlang:element(2, Node), erlang:element(3, Node), New_trivia}.
-file("src/greenwood.gleam", 726).
?DOC(
" Get all trivia tokens (leading then trailing) from a node.\n"
"\n"
" `trivia`\n"
).
-spec all_trivia(node_(DQZ)) -> list(token(DQZ)).
all_trivia(Node) ->
case erlang:element(4, Node) of
{trivia, Leading, Trailing} ->
lists:append(Leading, Trailing);
bare ->
[]
end.
-file("src/greenwood.gleam", 758).
?DOC(
" Create a zipper focused on the root node.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.zip` instead.\n"
).
-spec zip(node_(DRL)) -> zipper(DRL).
zip(Root) ->
{zipper, Root, []}.
-file("src/greenwood.gleam", 1170).
?DOC(
" `left` is accumulated nearest-first (reversed from source order) so that\n"
" `left`/`right` cursor moves are O(1) and `up` can rebuild children with a\n"
" single `list.reverse`.\n"
).
-spec split_at_node(
list(element(DXB)),
fun((node_(DXB)) -> boolean()),
list(element(DXB))
) -> gleam@option:option({list(element(DXB)), node_(DXB), list(element(DXB))}).
split_at_node(Elements, Predicate, Left) ->
case Elements of
[] ->
none;
[{node_element, N} | Rest] ->
gleam@bool:guard(
Predicate(N),
{some, {Left, N, Rest}},
fun() ->
split_at_node(Rest, Predicate, [{node_element, N} | Left])
end
);
[Other | Rest@1] ->
split_at_node(Rest@1, Predicate, [Other | Left])
end.
-file("src/greenwood.gleam", 1152).
-spec do_down(
node_(DWS),
fun((node_(DWS)) -> boolean()),
list(crumb(DWS)),
list(element(DWS))
) -> gleam@option:option(zipper(DWS)).
do_down(Parent, Predicate, Crumbs, Left) ->
case split_at_node(erlang:element(3, Parent), Predicate, Left) of
{some, {Left@1, Child, Right}} ->
Crumb = {crumb,
erlang:element(2, Parent),
erlang:element(4, Parent),
Left@1,
Right},
{some, {zipper, Child, [Crumb | Crumbs]}};
none ->
none
end.
-file("src/greenwood.gleam", 767).
?DOC(
" Move focus to the first child that is a Node.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.down` instead.\n"
).
-spec down(zipper(DRO)) -> gleam@option:option(zipper(DRO)).
down(Zipper) ->
do_down(
erlang:element(2, Zipper),
fun(_) -> true end,
erlang:element(3, Zipper),
[]
).
-file("src/greenwood.gleam", 776).
?DOC(
" Move focus to the first child Node matching a predicate.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.down_where` instead.\n"
).
-spec down_where(zipper(DRS), fun((node_(DRS)) -> boolean())) -> gleam@option:option(zipper(DRS)).
down_where(Zipper, Predicate) ->
do_down(erlang:element(2, Zipper), Predicate, erlang:element(3, Zipper), []).
-file("src/greenwood.gleam", 1189).
?DOC(
" Walk `elements` (the crumb's nearest-first `left` list) looking for the\n"
" first matching Node. Elements skipped along the way are prepended to\n"
" `right` so they end up positioned between the new focus and the prior\n"
" focus in source order.\n"
).
-spec scan_left(
list(element(DXN)),
fun((node_(DXN)) -> boolean()),
list(element(DXN))
) -> gleam@option:option({list(element(DXN)), node_(DXN), list(element(DXN))}).
scan_left(Elements, Predicate, Right) ->
case Elements of
[] ->
none;
[{node_element, N} | Rest] ->
gleam@bool:guard(
Predicate(N),
{some, {Rest, N, Right}},
fun() ->
scan_left(Rest, Predicate, [{node_element, N} | Right])
end
);
[Other | Rest@1] ->
scan_left(Rest@1, Predicate, [Other | Right])
end.
-file("src/greenwood.gleam", 1092).
-spec do_left_where(zipper(DWE), fun((node_(DWE)) -> boolean())) -> gleam@option:option(zipper(DWE)).
do_left_where(Zipper, Predicate) ->
case erlang:element(3, Zipper) of
[] ->
none;
[Crumb | Rest] ->
case scan_left(
erlang:element(4, Crumb),
Predicate,
[{node_element, erlang:element(2, Zipper)} |
erlang:element(5, Crumb)]
) of
{some, {New_left, New_focus, New_right}} ->
New_crumb = {crumb,
erlang:element(2, Crumb),
erlang:element(3, Crumb),
New_left,
New_right},
{some, {zipper, New_focus, [New_crumb | Rest]}};
none ->
none
end
end.
-file("src/greenwood.gleam", 1088).
-spec do_left(zipper(DWA)) -> gleam@option:option(zipper(DWA)).
do_left(Zipper) ->
do_left_where(Zipper, fun(_) -> true end).
-file("src/greenwood.gleam", 790).
?DOC(
" Move focus to the nearest sibling Node to the left of the focus.\n"
"\n"
" Returns `None` if the focus is the root or has no Node sibling to the left.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.left` instead.\n"
).
-spec left(zipper(DRX)) -> gleam@option:option(zipper(DRX)).
left(Zipper) ->
do_left(Zipper).
-file("src/greenwood.gleam", 802).
?DOC(
" Move focus to the nearest sibling Node to the left matching a predicate.\n"
"\n"
" Token siblings (whitespace, comments stored inline) are skipped and remain\n"
" in place between the old and new focus.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.left_where` instead.\n"
).
-spec left_where(zipper(DSB), fun((node_(DSB)) -> boolean())) -> gleam@option:option(zipper(DSB)).
left_where(Zipper, Predicate) ->
do_left_where(Zipper, Predicate).
-file("src/greenwood.gleam", 1208).
?DOC(
" Walk `elements` (the crumb's source-order `right` list) looking for the\n"
" first matching Node. Elements skipped along the way are prepended to\n"
" `left` (which is maintained nearest-first) so they end up positioned\n"
" between the prior focus and the new focus in source order.\n"
).
-spec scan_right(
list(element(DXZ)),
fun((node_(DXZ)) -> boolean()),
list(element(DXZ))
) -> gleam@option:option({list(element(DXZ)), node_(DXZ), list(element(DXZ))}).
scan_right(Elements, Predicate, Left) ->
case Elements of
[] ->
none;
[{node_element, N} | Rest] ->
gleam@bool:guard(
Predicate(N),
{some, {Left, N, Rest}},
fun() ->
scan_right(Rest, Predicate, [{node_element, N} | Left])
end
);
[Other | Rest@1] ->
scan_right(Rest@1, Predicate, [Other | Left])
end.
-file("src/greenwood.gleam", 1124).
-spec do_right_where(zipper(DWN), fun((node_(DWN)) -> boolean())) -> gleam@option:option(zipper(DWN)).
do_right_where(Zipper, Predicate) ->
case erlang:element(3, Zipper) of
[] ->
none;
[Crumb | Rest] ->
case scan_right(
erlang:element(5, Crumb),
Predicate,
[{node_element, erlang:element(2, Zipper)} |
erlang:element(4, Crumb)]
) of
{some, {New_left, New_focus, New_right}} ->
New_crumb = {crumb,
erlang:element(2, Crumb),
erlang:element(3, Crumb),
New_left,
New_right},
{some, {zipper, New_focus, [New_crumb | Rest]}};
none ->
none
end
end.
-file("src/greenwood.gleam", 1120).
-spec do_right(zipper(DWJ)) -> gleam@option:option(zipper(DWJ)).
do_right(Zipper) ->
do_right_where(Zipper, fun(_) -> true end).
-file("src/greenwood.gleam", 816).
?DOC(
" Move focus to the nearest sibling Node to the right of the focus.\n"
"\n"
" Returns `None` if the focus is the root or has no Node sibling to the right.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.right` instead.\n"
).
-spec right(zipper(DSG)) -> gleam@option:option(zipper(DSG)).
right(Zipper) ->
do_right(Zipper).
-file("src/greenwood.gleam", 828).
?DOC(
" Move focus to the nearest sibling Node to the right matching a predicate.\n"
"\n"
" Token siblings (whitespace, comments stored inline) are skipped and remain\n"
" in place between the old and new focus.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.right_where` instead.\n"
).
-spec right_where(zipper(DSK), fun((node_(DSK)) -> boolean())) -> gleam@option:option(zipper(DSK)).
right_where(Zipper, Predicate) ->
do_right_where(Zipper, Predicate).
-file("src/greenwood.gleam", 1073).
-spec do_up(zipper(DVW)) -> gleam@option:option(zipper(DVW)).
do_up(Zipper) ->
case erlang:element(3, Zipper) of
[] ->
none;
[Crumb | Rest] ->
Children = lists:append(
lists:reverse(erlang:element(4, Crumb)),
[{node_element, erlang:element(2, Zipper)} |
erlang:element(5, Crumb)]
),
Parent = {node,
erlang:element(2, Crumb),
Children,
erlang:element(3, Crumb)},
{some, {zipper, Parent, Rest}}
end.
-file("src/greenwood.gleam", 840).
?DOC(
" Move focus back up to the parent.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.up` instead.\n"
).
-spec up(zipper(DSP)) -> gleam@option:option(zipper(DSP)).
up(Zipper) ->
do_up(Zipper).
-file("src/greenwood.gleam", 1223).
-spec repeat_move(
zipper(DYL),
integer(),
fun((zipper(DYL)) -> gleam@option:option(zipper(DYL)))
) -> gleam@option:option(zipper(DYL)).
repeat_move(Zipper, N, Move) ->
case N of
_ when N < 0 ->
none;
0 ->
{some, Zipper};
_ ->
case Move(Zipper) of
{some, Z} ->
repeat_move(Z, N - 1, Move);
none ->
none
end
end.
-file("src/greenwood.gleam", 852).
?DOC(
" Move focus up `n` parents. Strict: returns `None` if `n` is negative or if\n"
" the cursor would move above the root.\n"
"\n"
" `up_n(z, by: 0)` returns `Some(z)`.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.up_n` instead.\n"
).
-spec up_n(zipper(DST), integer()) -> gleam@option:option(zipper(DST)).
up_n(Zipper, N) ->
repeat_move(Zipper, N, fun do_up/1).
-file("src/greenwood.gleam", 865).
?DOC(
" Move focus `n` sibling Nodes to the left. Strict: returns `None` if the\n"
" move cannot be completed in full.\n"
"\n"
" `left_n(z, by: 0)` returns `Some(z)`. Negative `n` flips direction:\n"
" `left_n(z, by: -n)` is equivalent to `right_n(z, by: n)`.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.left_n` instead.\n"
).
-spec left_n(zipper(DSX), integer()) -> gleam@option:option(zipper(DSX)).
left_n(Zipper, N) ->
gleam@bool:guard(
N < 0,
repeat_move(Zipper, - N, fun do_right/1),
fun() -> repeat_move(Zipper, N, fun do_left/1) end
).
-file("src/greenwood.gleam", 1057).
-spec do_right_n_where(zipper(DVR), integer(), fun((node_(DVR)) -> boolean())) -> gleam@option:option(zipper(DVR)).
do_right_n_where(Zipper, N, Predicate) ->
case N of
0 ->
{some, Zipper};
_ when N < 0 ->
do_left_n_where(Zipper, - N, Predicate);
_ ->
case do_right_where(Zipper, Predicate) of
{some, Z} ->
do_right_n_where(Z, N - 1, Predicate);
none ->
none
end
end.
-file("src/greenwood.gleam", 1041).
-spec do_left_n_where(zipper(DVM), integer(), fun((node_(DVM)) -> boolean())) -> gleam@option:option(zipper(DVM)).
do_left_n_where(Zipper, N, Predicate) ->
case N of
0 ->
{some, Zipper};
_ when N < 0 ->
do_right_n_where(Zipper, - N, Predicate);
_ ->
case do_left_where(Zipper, Predicate) of
{some, Z} ->
do_left_n_where(Z, N - 1, Predicate);
none ->
none
end
end.
-file("src/greenwood.gleam", 884).
?DOC(
" Move focus `n` sibling Nodes to the left where those nodes match\n"
" a predicate. Strict: returns `None` if the move cannot be completed in full.\n"
"\n"
" Token siblings (whitespace, comments stored inline) are skipped and remain\n"
" in place between the old and new focus.\n"
"\n"
" `left_n_where(zipper:, by: 0, predicate:)` returns `Some(zipper)`. Negative\n"
" `n` flips direction: `left_n_where(zipper:, by: -n, predicate:)` is\n"
" equivalent to `right_n_where(zipper:, by: n, predicate:)`\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.left_n_where` instead.\n"
).
-spec left_n_where(zipper(DTB), integer(), fun((node_(DTB)) -> boolean())) -> gleam@option:option(zipper(DTB)).
left_n_where(Zipper, N, Predicate) ->
do_left_n_where(Zipper, N, Predicate).
-file("src/greenwood.gleam", 901).
?DOC(
" Move focus `n` sibling Nodes to the right. Strict: returns `None` if the\n"
" move cannot be completed in full.\n"
"\n"
" `right_n(z, by: 0)` returns `Some(z)`. Negative `n` flips direction:\n"
" `right_n(z, by: -n)` is equivalent to `left_n(z, by: n)`.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.right_n` instead.\n"
).
-spec right_n(zipper(DTG), integer()) -> gleam@option:option(zipper(DTG)).
right_n(Zipper, N) ->
gleam@bool:guard(
N < 0,
repeat_move(Zipper, - N, fun do_left/1),
fun() -> repeat_move(Zipper, N, fun do_right/1) end
).
-file("src/greenwood.gleam", 920).
?DOC(
" Move focus `n` sibling Nodes to the right where those nodes match\n"
" a predicate. Strict: returns `None` if the move cannot be completed in full.\n"
"\n"
" Token siblings (whitespace, comments stored inline) are skipped and remain\n"
" in place between the old and new focus.\n"
"\n"
" `right_n_where(zipper:, by: 0, predicate:)` returns `Some(zipper)`. Negative\n"
" `n` flips direction: `right_n_where(zipper:, by: -n, predicate:)` is\n"
" equivalent to `left_n_where(zipper:, by: n, predicate:)`\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.right_n_where` instead.\n"
).
-spec right_n_where(zipper(DTK), integer(), fun((node_(DTK)) -> boolean())) -> gleam@option:option(zipper(DTK)).
right_n_where(Zipper, N, Predicate) ->
do_right_n_where(Zipper, N, Predicate).
-file("src/greenwood.gleam", 933).
?DOC(
" Replace the focused node and return the updated zipper.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.set_focus` instead.\n"
).
-spec set_focus(zipper(DTP), node_(DTP)) -> zipper(DTP).
set_focus(Zipper, Node) ->
{zipper, Node, erlang:element(3, Zipper)}.
-file("src/greenwood.gleam", 945).
?DOC(
" Apply a transform to the focused node.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.map_focus` instead.\n"
).
-spec map_focus(zipper(DTT), fun((node_(DTT)) -> node_(DTT))) -> zipper(DTT).
map_focus(Zipper, F) ->
{zipper, F(erlang:element(2, Zipper)), erlang:element(3, Zipper)}.
-file("src/greenwood.gleam", 1034).
-spec do_unzip(zipper(DVJ)) -> node_(DVJ).
do_unzip(Zipper) ->
case do_up(Zipper) of
{some, Parent_zipper} ->
do_unzip(Parent_zipper);
none ->
erlang:element(2, Zipper)
end.
-file("src/greenwood.gleam", 957).
?DOC(
" Reconstruct the full tree from a zipper by moving up to the root.\n"
"\n"
" `cursor`\n"
"\n"
" _Deprecated_: Use `greenwood/zipper.unzip` instead.\n"
).
-spec unzip(zipper(DTY)) -> node_(DTY).
unzip(Zipper) ->
do_unzip(Zipper).