#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use tree_sitter::Node as OtherNode;
use tree_sitter::Tree as OtherTree;
use tree_sitter::{Parser, TreeCursor};
use crate::checker::Checker;
use crate::traits::{LanguageInfo, Search};
#[derive(Clone, Debug)]
pub(crate) struct Tree(OtherTree);
impl Tree {
pub(crate) fn new<T: LanguageInfo>(code: &[u8]) -> Self {
let mut parser = Parser::new();
let language = T::get_lang().get_ts_language().expect(
"invariant: dispatcher cfg-gates this call behind the per-language Cargo feature",
);
parser
.set_language(&language)
.expect("invariant: grammar version is pinned and compatible with bundled tree-sitter");
Self(
parser
.parse(code, None)
.expect("invariant: parser has a language set and no cancellation flag"),
)
}
pub(crate) fn from_ts_tree(tree: OtherTree) -> Self {
Self(tree)
}
pub(crate) fn get_root(&self) -> Node<'_> {
Node(self.0.root_node())
}
pub(crate) fn as_ts_tree(&self) -> &OtherTree {
&self.0
}
}
#[derive(Clone, Copy, Debug)]
pub struct Node<'a>(pub OtherNode<'a>);
impl<'a> Node<'a> {
#[must_use]
pub fn has_error(&self) -> bool {
self.0.has_error()
}
pub(crate) fn id(&self) -> usize {
self.0.id()
}
pub(crate) fn kind(&self) -> &'static str {
self.0.kind()
}
pub(crate) fn kind_id(&self) -> u16 {
self.0.kind_id()
}
pub(crate) fn utf8_text(&self, data: &'a [u8]) -> Option<&'a str> {
self.0.utf8_text(data).ok()
}
pub(crate) fn start_byte(&self) -> usize {
self.0.start_byte()
}
pub(crate) fn end_byte(&self) -> usize {
self.0.end_byte()
}
pub(crate) fn start_position(&self) -> (usize, usize) {
let temp = self.0.start_position();
(temp.row, temp.column)
}
pub(crate) fn end_position(&self) -> (usize, usize) {
let temp = self.0.end_position();
(temp.row, temp.column)
}
pub(crate) fn start_row(&self) -> usize {
self.0.start_position().row
}
pub(crate) fn end_row(&self) -> usize {
self.0.end_position().row
}
pub(crate) fn parent(&self) -> Option<Node<'a>> {
self.0.parent().map(Node)
}
#[inline]
pub(crate) fn has_sibling(&self, id: u16) -> bool {
self.0.parent().is_some_and(|parent| {
parent
.children(&mut parent.walk())
.any(|child| child.kind_id() == id)
})
}
pub(crate) fn previous_sibling(&self) -> Option<Node<'a>> {
self.0.prev_sibling().map(Node)
}
#[inline]
pub(crate) fn is_child(&self, id: u16) -> bool {
let mut cur = self.0.child(0);
while let Some(c) = cur {
if c.kind_id() == id {
return true;
}
cur = c.next_sibling();
}
false
}
pub(crate) fn child_count(&self) -> usize {
self.0.child_count()
}
pub(crate) fn is_named(&self) -> bool {
self.0.is_named()
}
pub(crate) fn child_by_field_name(&self, name: &str) -> Option<Node<'_>> {
self.0.child_by_field_name(name).map(Node)
}
pub(crate) fn child(&self, pos: usize) -> Option<Node<'a>> {
self.0.child(pos as u32).map(Node)
}
pub(crate) fn field_name_for_child(&self, child_index: u32) -> Option<&'static str> {
self.0.field_name_for_child(child_index)
}
pub(crate) fn children(&self) -> impl ExactSizeIterator<Item = Node<'a>> + use<'a> {
let mut cursor = self.cursor();
cursor.goto_first_child();
(0..self.child_count()).map(move |_| {
let result = cursor.node();
cursor.goto_next_sibling();
result
})
}
pub(crate) fn cursor(&self) -> Cursor<'a> {
Cursor(self.0.walk())
}
#[allow(dead_code)]
pub(crate) fn get_parent(&self, level: usize) -> Option<Node<'a>> {
let mut level = level;
let mut node = *self;
while level != 0 {
if let Some(parent) = node.parent() {
node = parent;
} else {
return None;
}
level -= 1;
}
Some(node)
}
pub(crate) fn count_specific_ancestors<T: crate::ParserTrait>(
&self,
check: fn(&Node) -> bool,
stop: fn(&Node) -> bool,
) -> usize {
let mut count = 0;
let mut node = *self;
while let Some(parent) = node.parent() {
if stop(&parent) {
break;
}
if check(&parent) && !T::Checker::is_else_if(&parent) {
count += 1;
}
node = parent;
}
count
}
pub(crate) fn parent_grandparent_match(
&self,
parent_pred: fn(&Node) -> bool,
grand_pred: fn(&Node) -> bool,
) -> bool {
let Some(parent) = self.parent() else {
return false;
};
if !parent_pred(&parent) {
return false;
}
let Some(grand) = parent.parent() else {
return false;
};
grand_pred(&grand)
}
}
#[derive(Clone)]
pub struct Cursor<'a>(TreeCursor<'a>);
impl<'a> Cursor<'a> {
pub(crate) fn reset(&mut self, node: &Node<'a>) {
self.0.reset(node.0);
}
pub(crate) fn goto_next_sibling(&mut self) -> bool {
self.0.goto_next_sibling()
}
pub(crate) fn goto_first_child(&mut self) -> bool {
self.0.goto_first_child()
}
pub(crate) fn node(&self) -> Node<'a> {
Node(self.0.node())
}
}
impl<'a> Search<'a> for Node<'a> {
fn first_occurrence(&self, pred: fn(u16) -> bool) -> Option<Node<'a>> {
let mut cursor = self.cursor();
let mut stack = Vec::new();
let mut children = Vec::new();
stack.push(*self);
while let Some(node) = stack.pop() {
if pred(node.kind_id()) {
return Some(node);
}
cursor.reset(&node);
if cursor.goto_first_child() {
loop {
children.push(cursor.node());
if !cursor.goto_next_sibling() {
break;
}
}
for child in children.drain(..).rev() {
stack.push(child);
}
}
}
None
}
fn act_on_node(&self, action: &mut dyn FnMut(&Node<'a>)) {
let mut cursor = self.cursor();
let mut stack = Vec::new();
let mut children = Vec::new();
stack.push(*self);
while let Some(node) = stack.pop() {
action(&node);
cursor.reset(&node);
if cursor.goto_first_child() {
loop {
children.push(cursor.node());
if !cursor.goto_next_sibling() {
break;
}
}
for child in children.drain(..).rev() {
stack.push(child);
}
}
}
}
fn first_child(&self, pred: fn(u16) -> bool) -> Option<Node<'a>> {
self.children().find(|&child| pred(child.kind_id()))
}
fn act_on_child(&self, action: &mut dyn FnMut(&Node<'a>)) {
for child in self.children() {
action(&child);
}
}
}