Skip to main content

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(&current.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(&current.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}