tree_sitter_utils/combinators/climb.rs
1//! The [`Climb`] and [`OrElseClimb`] combinators — walk up the syntax tree.
2
3use crate::handler::{Handler, HandlerResult};
4use crate::input::Input;
5
6/// A handler that retries itself on each ancestor node, stopping when it
7/// succeeds, when it reaches a node whose kind is in `stop_kinds`, or when
8/// there are no more parents.
9///
10/// The first attempt is made on the **original** node; climbing starts only
11/// if that attempt returns `None`.
12///
13/// Constructed via [`HandlerExt::climb`](crate::HandlerExt::climb).
14///
15/// # Example
16///
17/// ```rust
18/// use tree_sitter_utils::{handler_fn, HandlerExt, Input};
19///
20/// // Climbs until it finds a node whose kind is "module" or hits the root.
21/// let h = (|input: tree_sitter_utils::Input<()>| -> Option<String> {
22/// (input.node.kind() == "module").then(|| "found module".to_owned())
23/// })
24/// .climb(&["source_file"]);
25/// let _ = h;
26/// ```
27pub struct Climb<H> {
28 /// The handler tried at each level.
29 pub inner: H,
30 /// Kinds at which climbing stops (regardless of success).
31 pub stop_kinds: &'static [&'static str],
32}
33
34impl<Ctx, R, H> Handler<Ctx, R> for Climb<H>
35where
36 H: Handler<Ctx, R>,
37 Ctx: Copy,
38{
39 fn handle<'tree>(&self, input: Input<'tree, Ctx>) -> HandlerResult<R> {
40 let mut current = input.node;
41 loop {
42 let attempt = self.inner.handle(input.with_node(current));
43 if attempt.is_some() {
44 return attempt;
45 }
46 // Stop if the current node is a stop-kind.
47 if self.stop_kinds.contains(¤t.kind()) {
48 return None;
49 }
50 match current.parent() {
51 Some(parent) => current = parent,
52 None => return None,
53 }
54 }
55 }
56}
57
58/// A handler that first tries `inner` on the current node; if that returns
59/// `None`, walks up the tree trying `other` at each ancestor, stopping at
60/// any kind in `stop_kinds` or at the root.
61///
62/// Constructed via [`HandlerExt::or_else_climb`](crate::HandlerExt::or_else_climb).
63///
64/// # Example
65///
66/// ```rust
67/// use tree_sitter_utils::{never, HandlerExt, Input};
68///
69/// let leaf = never::<(), String>();
70/// let h = leaf.or_else_climb(
71/// |inp: tree_sitter_utils::Input<()>| -> Option<String> {
72/// (inp.node.kind() == "module").then(|| "module".to_owned())
73/// },
74/// &["source_file"],
75/// );
76/// let _ = h;
77/// ```
78pub struct OrElseClimb<H, O> {
79 /// Handler tried first on the original node.
80 pub inner: H,
81 /// Handler tried on each ancestor when `inner` fails.
82 pub other: O,
83 /// Kinds at which climbing stops.
84 pub stop_kinds: &'static [&'static str],
85}
86
87impl<Ctx, R, H, O> Handler<Ctx, R> for OrElseClimb<H, O>
88where
89 H: Handler<Ctx, R>,
90 O: Handler<Ctx, R>,
91 Ctx: Copy,
92{
93 fn handle<'tree>(&self, input: Input<'tree, Ctx>) -> HandlerResult<R> {
94 // First try `inner` on the original node.
95 if let Some(out) = self.inner.handle(input) {
96 return Some(out);
97 }
98 // Then climb with `other`.
99 let mut current = input.node;
100 loop {
101 if self.stop_kinds.contains(¤t.kind()) {
102 return None;
103 }
104 match current.parent() {
105 Some(parent) => {
106 current = parent;
107 let attempt = self.other.handle(input.with_node(current));
108 if attempt.is_some() {
109 return attempt;
110 }
111 }
112 None => return None,
113 }
114 }
115 }
116}