#![cfg_attr(feature = "strict_docs", allow(missing_docs))]
use crate::ffi::TSSymbol;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct ParsedChild {
pub node: ParsedNode,
pub field_id: Option<u16>,
}
impl ParsedChild {
pub fn field_name<'a>(&self, language: &'a TSLanguage) -> Option<&'a str> {
self.field_id.and_then(|id| language.field_name(id))
}
}
#[derive(Clone)]
pub struct ParsedNode {
pub symbol: TSSymbol,
pub children: Vec<ParsedChild>,
pub start_byte: usize,
pub end_byte: usize,
pub start_point: Point,
pub end_point: Point,
pub is_extra: bool,
pub is_error: bool,
pub is_missing: bool,
pub is_named: bool,
pub language: Option<Arc<TSLanguage>>,
}
impl ParsedNode {
pub fn child_by_field_name<'a>(&'a self, name: &str) -> Option<&'a ParsedNode> {
let language = self.language.as_ref()?;
let field_id = language.field_id_for_name(name)?;
self.children
.iter()
.find(|c| c.field_id == Some(field_id))
.map(|c| &c.node)
}
pub fn children_by_field_name<'a>(&'a self, name: &str) -> Vec<&'a ParsedNode> {
let language = match self.language.as_ref() {
Some(l) => l,
None => return Vec::new(),
};
let field_id = match language.field_id_for_name(name) {
Some(id) => id,
None => return Vec::new(),
};
self.children
.iter()
.filter(|c| c.field_id == Some(field_id))
.map(|c| &c.node)
.collect()
}
pub fn kind<'a>(&self, language: &'a TSLanguage) -> &'a str {
language.symbol_name(self.symbol)
}
pub fn named_children(&self) -> impl Iterator<Item = &ParsedChild> {
self.children.iter().filter(|c| c.node.is_named)
}
pub fn child(&self, index: usize) -> Option<&ParsedNode> {
self.children.get(index).map(|c| &c.node)
}
pub fn child_count(&self) -> usize {
self.children.len()
}
}
impl std::fmt::Debug for ParsedNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ParsedNode")
.field("symbol", &self.symbol)
.field("children", &self.children)
.field("start_byte", &self.start_byte)
.field("end_byte", &self.end_byte)
.field("start_point", &self.start_point)
.field("end_point", &self.end_point)
.field("is_extra", &self.is_extra)
.field("is_error", &self.is_error)
.field("is_missing", &self.is_missing)
.field("is_named", &self.is_named)
.field("has_language", &self.language.is_some())
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Point {
pub row: usize,
pub column: usize,
}
impl Point {
pub fn new(row: usize, column: usize) -> Self {
Point { row, column }
}
}
pub struct TSLanguage {
pub field_names: Vec<&'static str>,
pub symbol_names: Vec<&'static str>,
pub production_field_map: Vec<Vec<Option<u16>>>,
}
impl TSLanguage {
pub fn field_id_for_name(&self, name: &str) -> Option<u16> {
self.field_names
.binary_search_by_key(&name, |&n| n)
.ok()
.map(|idx| idx as u16)
}
pub fn field_name(&self, id: u16) -> Option<&'static str> {
self.field_names.get(id as usize).copied()
}
pub fn symbol_name(&self, symbol: TSSymbol) -> &'static str {
self.symbol_names
.get(symbol as usize)
.copied()
.unwrap_or("ERROR")
}
pub fn production_fields(&self, production_id: u16) -> &[Option<u16>] {
self.production_field_map
.get(production_id as usize)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
}
impl ParsedNode {
pub fn from_reduction(
symbol: TSSymbol,
production_id: u16,
children: Vec<ParsedNode>,
language: Arc<TSLanguage>,
) -> Self {
let field_map = language.production_fields(production_id);
let parsed_children: Vec<ParsedChild> = children
.into_iter()
.enumerate()
.map(|(i, child)| ParsedChild {
node: child,
field_id: field_map.get(i).and_then(|&f| f),
})
.collect();
let (start_byte, end_byte) = if parsed_children.is_empty() {
(0, 0)
} else {
let start = parsed_children[0].node.start_byte;
let end = parsed_children
.last()
.map(|child| child.node.end_byte)
.unwrap_or(start);
(start, end)
};
let (start_point, end_point) = if parsed_children.is_empty() {
(Point::new(0, 0), Point::new(0, 0))
} else {
let start = parsed_children[0].node.start_point;
let end = parsed_children
.last()
.map(|child| child.node.end_point)
.unwrap_or(start);
(start, end)
};
ParsedNode {
symbol,
children: parsed_children,
start_byte,
end_byte,
start_point,
end_point,
is_extra: false,
is_error: false,
is_missing: false,
is_named: true, language: Some(language),
}
}
}