tree_sitter_utils/combinators/find_ancestor.rs
1//! The [`FindAncestor`] combinator and [`HandlerExt::find_ancestor`] method.
2//!
3//! This combinator solves the "find the nearest ancestor of a specific kind,
4//! then run a handler on *that* node" pattern, which is distinct from
5//! [`Climb`](super::climb::Climb).
6//!
7//! `Climb` retries **the same handler** on successive ancestors until it
8//! returns `Some`. `FindAncestor` first **locates** the nearest ancestor
9//! whose kind is in `target_kinds`, then runs `inner` exactly once on that
10//! found node. If no such ancestor exists, it returns `None`.
11//!
12//! ## Comparison table
13//!
14//! | Combinator | When to use |
15//! |---|---|
16//! | `.climb(stop)` | Retry `self` on each ancestor until it succeeds. |
17//! | `.find_ancestor(kinds, stop)` | Find the nearest ancestor of a specific kind, then run `self` on it once. |
18
19use crate::handler::{Handler, HandlerResult};
20use crate::input::Input;
21
22/// A handler that walks up the syntax tree to find the nearest strict
23/// ancestor whose kind is in `target_kinds`, then runs `inner` on that
24/// ancestor node.
25///
26/// **The original node is never tested** — only its ancestors are examined.
27///
28/// Walking stops (returning `None`) when:
29/// - a node whose kind is in `stop_kinds` is reached, or
30/// - the root is reached with no match.
31///
32/// Constructed via
33/// [`HandlerExt::find_ancestor`](crate::HandlerExt::find_ancestor).
34///
35/// # Example
36///
37/// ```rust
38/// use tree_sitter_utils::{handler_fn, HandlerExt, Input};
39///
40/// // Walk up to the nearest "argument_list" and return its kind.
41/// let h = handler_fn(|inp: Input<()>| inp.node.kind().to_owned())
42/// .find_ancestor(&["argument_list"], &["program"]);
43/// let _ = h;
44/// ```
45pub struct FindAncestor<H> {
46 /// The handler invoked on the found ancestor node.
47 pub inner: H,
48 /// The set of ancestor kinds to search for. The first ancestor whose
49 /// kind appears in this list wins.
50 pub target_kinds: &'static [&'static str],
51 /// Ancestor kinds at which the search stops unconditionally (the stop
52 /// node itself is **not** tested against `target_kinds`).
53 pub stop_kinds: &'static [&'static str],
54}
55
56impl<Ctx, R, H> Handler<Ctx, R> for FindAncestor<H>
57where
58 H: Handler<Ctx, R>,
59 Ctx: Copy,
60{
61 fn handle<'tree>(&self, input: Input<'tree, Ctx>) -> HandlerResult<R> {
62 let mut current = input.node.parent();
63 while let Some(ancestor) = current {
64 if self.stop_kinds.contains(&ancestor.kind()) {
65 return None;
66 }
67 if self.target_kinds.contains(&ancestor.kind()) {
68 return self.inner.handle(input.with_node(ancestor));
69 }
70 current = ancestor.parent();
71 }
72 None
73 }
74}