use tree_sitter::Node;
#[must_use]
pub fn first_named_child<'tree>(node: &Node<'tree>) -> Option<Node<'tree>> {
let mut cursor = node.walk();
let first = node.named_children(&mut cursor).next();
first
}
#[must_use]
pub fn first_named_child_of_kind<'tree>(node: &Node<'tree>, kind: &str) -> Option<Node<'tree>> {
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == kind {
return Some(child);
}
}
None
}
#[must_use]
pub fn first_identifier_descendant<'tree>(node: Node<'tree>) -> Option<Node<'tree>> {
let mut work_stack = vec![node];
while let Some(current) = work_stack.pop() {
if current != node && looks_like_identifier(current.kind()) {
return Some(current);
}
let mut cursor = current.walk();
let children: Vec<Node<'tree>> = current.named_children(&mut cursor).collect();
for child in children.into_iter().rev() {
work_stack.push(child);
}
}
None
}
#[must_use]
pub fn first_identifier_like_child<'tree>(node: &Node<'tree>) -> Option<Node<'tree>> {
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if looks_like_identifier(child.kind()) {
return Some(child);
}
}
None
}
#[inline]
#[must_use]
pub fn looks_like_identifier(kind: &str) -> bool {
kind.ends_with("identifier")
|| kind.ends_with("_name")
|| kind == "name"
|| kind == "word"
|| kind == "var"
}
#[inline]
#[must_use]
pub fn looks_like_bare_identifier(s: &str) -> bool {
!s.is_empty()
&& s.chars().next().is_some_and(|c| c.is_alphabetic() || c == '_')
&& s.chars().all(|c| c.is_alphanumeric() || c == '_')
}
#[cfg(test)]
#[path = "identifiers_tests.rs"]
mod tests;