use std::hash::{Hash, Hasher};
use std::num::NonZeroUsize;
use std::ops::Range;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SyntaxId(NonZeroUsize);
impl SyntaxId {
fn new(index: usize) -> Self {
Self(NonZeroUsize::new(index + 1).expect("index overflow"))
}
#[inline]
pub fn index(self) -> usize {
self.0.get() - 1
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxHint<'a> {
String,
Comment(&'a str),
Punctuation,
}
#[derive(Debug)]
pub struct SyntaxTree<'a> {
nodes: Vec<SyntaxNode<'a>>,
}
#[derive(Debug)]
pub struct SyntaxNode<'a> {
pub id: SyntaxId,
pub structural_hash: u64,
pub byte_range: Range<usize>,
pub hint: Option<SyntaxHint<'a>>,
pub delimiters: [Option<(Range<usize>, &'a str)>; 2],
pub descendant_count: usize,
pub depth: usize,
parent: Option<SyntaxId>,
}
impl<'a> SyntaxNode<'a> {
#[inline]
pub fn open_delimiter(&self) -> Option<&str> {
self.delimiters[0].as_ref().map(|d| d.1)
}
#[inline]
pub fn close_delimiter(&self) -> Option<&str> {
self.delimiters[1].as_ref().map(|d| d.1)
}
#[inline]
pub fn open_delimiter_range(&self) -> Option<Range<usize>> {
self.delimiters[0].as_ref().map(|d| d.0.clone())
}
#[inline]
pub fn close_delimiter_range(&self) -> Option<Range<usize>> {
self.delimiters[1].as_ref().map(|d| d.0.clone())
}
#[inline]
pub fn has_delimiters(&self) -> bool {
self.delimiters[0].is_some() && self.delimiters[1].is_some()
}
#[inline]
pub fn is_list(&self) -> bool {
self.descendant_count > 0
}
#[inline]
pub fn is_atom(&self) -> bool {
self.descendant_count == 0
}
}
impl<'a> SyntaxTree<'a> {
pub fn new() -> Self {
Self { nodes: Vec::new() }
}
pub fn root(&self) -> Option<SyntaxId> {
if self.nodes.is_empty() {
None
} else {
Some(SyntaxId::new(0))
}
}
#[inline]
pub fn get(&self, id: SyntaxId) -> &SyntaxNode<'a> {
&self.nodes[id.index()]
}
#[inline]
pub fn len(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn first_child(&self, id: SyntaxId) -> Option<SyntaxId> {
let node = self.get(id);
if node.descendant_count > 0 {
Some(SyntaxId::new(id.index() + 1))
} else {
None
}
}
pub fn next_sibling(&self, id: SyntaxId) -> Option<SyntaxId> {
let node = self.get(id);
let next_index = id.index() + 1 + node.descendant_count;
let parent_id = node.parent?;
let parent = self.get(parent_id);
let parent_end = parent_id.index() + 1 + parent.descendant_count;
if next_index < parent_end {
Some(SyntaxId::new(next_index))
} else {
None
}
}
#[inline]
pub fn parent(&self, id: SyntaxId) -> Option<SyntaxId> {
self.get(id).parent
}
pub fn preorder(&self) -> impl Iterator<Item = SyntaxId> + '_ {
(0..self.nodes.len()).map(SyntaxId::new)
}
pub fn cursor(&self) -> SyntaxTreeCursor<'_> {
SyntaxTreeCursor {
tree: self,
last: None,
current: self.root(),
}
}
pub fn cursor_at(&self, node: SyntaxId) -> SyntaxTreeCursor<'_> {
SyntaxTreeCursor {
tree: self,
last: None,
current: Some(node),
}
}
}
impl<'a> Default for SyntaxTree<'a> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug)]
pub struct SyntaxTreeCursor<'a> {
tree: &'a SyntaxTree<'a>,
last: Option<SyntaxId>,
current: Option<SyntaxId>,
}
impl<'a> SyntaxTreeCursor<'a> {
#[inline]
pub fn id(&self) -> Option<SyntaxId> {
self.current
}
#[inline]
pub fn node(&self) -> Option<&'a SyntaxNode<'_>> {
self.current.map(|id| self.tree.get(id))
}
#[inline]
pub fn is_end(&self) -> bool {
self.current.is_none()
}
#[inline]
pub fn tree(&self) -> &'a SyntaxTree<'_> {
self.tree
}
pub fn goto_first_child(&mut self) -> bool {
if let Some(id) = self.current {
if let Some(child) = self.tree.first_child(id) {
self.last = Some(id);
self.current = Some(child);
return true;
}
}
false
}
pub fn goto_next_sibling(&mut self) -> bool {
if let Some(id) = self.current {
if let Some(sibling) = self.tree.next_sibling(id) {
self.last = Some(id);
self.current = Some(sibling);
return true;
}
}
false
}
pub fn goto_parent(&mut self) -> bool {
if let Some(id) = self.current {
if let Some(parent) = self.tree.parent(id) {
self.last = Some(id);
self.current = Some(parent);
return true;
}
}
false
}
pub fn goto_last(&mut self) -> bool {
if let Some(id) = self.last {
self.last = self.current;
self.current = Some(id);
return true;
}
false
}
#[inline]
pub fn first_child(&self) -> Self {
Self {
tree: self.tree,
last: self.current.or(self.last),
current: self.current.and_then(|id| self.tree.first_child(id)),
}
}
#[inline]
pub fn next_sibling(&self) -> Self {
Self {
tree: self.tree,
last: self.current.or(self.last),
current: self.current.and_then(|id| self.tree.next_sibling(id)),
}
}
#[inline]
pub fn last(&self) -> Self {
Self {
tree: self.tree,
last: self.current,
current: self.last,
}
}
#[inline]
pub fn parent(&self) -> Self {
Self {
tree: self.tree,
last: self.current.or(self.last),
current: self.current.and_then(|id| self.tree.parent(id)),
}
}
#[inline]
pub fn depth(&self) -> usize {
self.current.map(|id| self.tree.get(id).depth).unwrap_or(0)
}
}
impl PartialEq for SyntaxTreeCursor<'_> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.tree, other.tree) && self.current == other.current
}
}
impl Eq for SyntaxTreeCursor<'_> {}
impl Hash for SyntaxTreeCursor<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.current.hash(state);
}
}
pub fn build_tree<'a>(mut cursor: tree_sitter::TreeCursor<'_>, source: &'a str) -> SyntaxTree<'a> {
let mut nodes = Vec::with_capacity(cursor.node().descendant_count());
if cursor.node().child_count() > 0 || !cursor.node().is_extra() {
build_tree_recursive(&mut cursor, &mut nodes, None, source);
}
SyntaxTree { nodes }
}
fn build_tree_recursive<'a>(
cursor: &mut tree_sitter::TreeCursor<'_>,
nodes: &mut Vec<SyntaxNode<'a>>,
parent: Option<SyntaxId>,
source: &'a str,
) -> SyntaxId {
let mut ts_node = cursor.node();
let this_id = SyntaxId::new(nodes.len());
let flattened = ts_node.child_count() == 1
&& cursor.goto_first_child()
&& ts_node.byte_range() == cursor.node().byte_range();
if flattened {
ts_node = cursor.node();
} else if cursor.node() != ts_node {
cursor.goto_parent();
}
nodes.push(SyntaxNode {
id: this_id,
structural_hash: 0,
byte_range: ts_node.byte_range(),
delimiters: [None, None],
hint: None,
descendant_count: 0,
depth: parent
.map(|parent| nodes[parent.index()].depth + 1)
.unwrap_or(0),
parent,
});
let mut hasher = std::hash::DefaultHasher::new();
ts_node.kind_id().hash(&mut hasher);
let mut remaining_children = ts_node.child_count();
let mut delimiters = [None, None];
let mut descendant_count = 0;
let mut hint = None;
if remaining_children >= 2 {
if let (Some(first_child), Some(last_child)) = (
ts_node.child(0),
ts_node.child((remaining_children - 1) as u32),
) {
if first_child.start_byte() == ts_node.start_byte()
&& last_child.end_byte() == ts_node.end_byte()
{
if let Some((open, close)) = detect_delimiters(first_child, last_child, source) {
open.hash(&mut hasher);
close.hash(&mut hasher);
delimiters[0] = Some((first_child.byte_range(), open));
delimiters[1] = Some((last_child.byte_range(), close));
remaining_children -= 2;
}
}
}
}
if cursor.goto_first_child() {
if delimiters[0].is_some() {
cursor.goto_next_sibling();
}
loop {
if remaining_children == 0 {
break;
}
let child_id = build_tree_recursive(cursor, nodes, Some(this_id), source);
let child_node = &nodes[child_id.index()];
remaining_children -= 1;
descendant_count += child_node.descendant_count + 1;
child_node.structural_hash.hash(&mut hasher);
if !cursor.goto_next_sibling() {
break;
}
}
cursor.goto_parent();
} else {
if let Some(source) = source.get(ts_node.byte_range()) {
source.hash(&mut hasher);
if source == "," || source == ";" || source == "." {
hint = Some(SyntaxHint::Punctuation);
} else if ts_node.is_extra() {
hint = Some(SyntaxHint::Comment(source));
}
}
}
if flattened {
cursor.goto_parent();
}
let node = &mut nodes[this_id.index()];
node.structural_hash = hasher.finish();
node.delimiters = delimiters;
node.descendant_count = descendant_count;
node.hint = hint;
this_id
}
fn detect_delimiters<'a>(
first_child: tree_sitter::Node<'_>,
last_child: tree_sitter::Node<'_>,
source: &'a str,
) -> Option<(&'a str, &'a str)> {
if first_child.child_count() != 0 || last_child.child_count() != 0 {
return None;
}
let is_delimiter = |delimiter: &str| {
!delimiter.is_empty()
&& delimiter.len() <= 2
&& !delimiter.chars().any(|c| c.is_alphanumeric())
};
let open = source.get(first_child.byte_range())?;
let close = source.get(last_child.byte_range())?;
if !is_delimiter(open) || !is_delimiter(close) {
return None;
}
Some((open, close))
}