use std::num::NonZeroU16;
use arborium_tree_sitter::{Node, TreeCursor};
use plotnik_bytecode::Nav;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SkipPolicy {
Any,
Trivia,
Exact,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UpMode {
Any,
SkipTrivia,
Exact,
}
pub struct CursorWrapper<'t> {
cursor: TreeCursor<'t>,
trivia_types: Vec<u16>,
}
impl<'t> CursorWrapper<'t> {
pub fn new(cursor: TreeCursor<'t>, trivia_types: Vec<u16>) -> Self {
Self {
cursor,
trivia_types,
}
}
#[inline]
pub fn node(&self) -> Node<'t> {
self.cursor.node()
}
#[inline]
pub fn descendant_index(&self) -> u32 {
self.cursor.descendant_index() as u32
}
#[inline]
pub fn goto_descendant(&mut self, index: u32) {
self.cursor.goto_descendant(index as usize);
}
#[inline]
pub fn field_id(&self) -> Option<NonZeroU16> {
self.cursor.field_id()
}
#[inline]
pub fn depth(&self) -> u32 {
self.cursor.depth()
}
#[inline]
pub fn goto_parent(&mut self) -> bool {
self.cursor.goto_parent()
}
#[inline]
pub fn is_trivia(&self, node: &Node<'_>) -> bool {
!node.is_named() || self.trivia_types.contains(&node.kind_id())
}
pub fn navigate(&mut self, nav: Nav) -> Option<SkipPolicy> {
match nav {
Nav::Epsilon => {
debug_assert!(
false,
"navigate called with Epsilon - should be skipped by VM"
);
Some(SkipPolicy::Any)
}
Nav::Stay => Some(SkipPolicy::Any),
Nav::StayExact => Some(SkipPolicy::Exact),
Nav::Down => self.go_first_child().then_some(SkipPolicy::Any),
Nav::DownSkip => self.go_first_child().then_some(SkipPolicy::Trivia),
Nav::DownExact => self.go_first_child().then_some(SkipPolicy::Exact),
Nav::Next => self.go_next_sibling().then_some(SkipPolicy::Any),
Nav::NextSkip => self.go_next_sibling().then_some(SkipPolicy::Trivia),
Nav::NextExact => self.go_next_sibling().then_some(SkipPolicy::Exact),
Nav::Up(n) => self.go_up(n, UpMode::Any).then_some(SkipPolicy::Any),
Nav::UpSkipTrivia(n) => self.go_up(n, UpMode::SkipTrivia).then_some(SkipPolicy::Any),
Nav::UpExact(n) => self.go_up(n, UpMode::Exact).then_some(SkipPolicy::Any),
}
}
fn go_first_child(&mut self) -> bool {
self.cursor.goto_first_child()
}
fn go_next_sibling(&mut self) -> bool {
self.cursor.goto_next_sibling()
}
fn go_up(&mut self, levels: u8, mode: UpMode) -> bool {
match mode {
UpMode::Any => {}
UpMode::Exact => {
if self.cursor.goto_next_sibling() {
self.cursor.goto_previous_sibling();
return false;
}
}
UpMode::SkipTrivia => {
let saved = self.cursor.descendant_index();
while self.cursor.goto_next_sibling() {
if !self.is_trivia(&self.cursor.node()) {
self.cursor.goto_descendant(saved);
return false;
}
}
self.cursor.goto_descendant(saved);
}
}
for _ in 0..levels {
if !self.cursor.goto_parent() {
return false;
}
}
true
}
pub fn continue_search(&mut self, policy: SkipPolicy) -> bool {
match policy {
SkipPolicy::Exact => false,
SkipPolicy::Trivia => {
if !self.is_trivia(&self.cursor.node()) {
return false;
}
self.cursor.goto_next_sibling()
}
SkipPolicy::Any => self.cursor.goto_next_sibling(),
}
}
}