use cairo_lang_filesystem::span::TextWidth;
use cairo_lang_primitive_token::{PrimitiveSpan, PrimitiveToken, ToPrimitiveTokenStream};
use salsa::Database;
use super::SyntaxNode;
pub struct SyntaxNodeWithDb<'a> {
node: &'a SyntaxNode<'a>,
db: &'a dyn Database,
}
impl<'a> SyntaxNodeWithDb<'a> {
pub fn new(node: &'a SyntaxNode<'a>, db: &'a dyn Database) -> Self {
Self { node, db }
}
}
impl<'a> ToPrimitiveTokenStream for SyntaxNodeWithDb<'a> {
type Iter = SyntaxNodeWithDbIterator<'a>;
fn to_primitive_token_stream(&self) -> Self::Iter {
SyntaxNodeWithDbIterator::new(self.db, self.node)
}
}
pub struct SyntaxNodeWithDbIterator<'a> {
iter_stack: Vec<&'a SyntaxNode<'a>>,
buffer: Vec<PrimitiveToken>,
db: &'a dyn Database,
}
impl<'a> SyntaxNodeWithDbIterator<'a> {
pub fn new(db: &'a dyn Database, node: &'a SyntaxNode<'a>) -> Self {
Self { db, buffer: Vec::with_capacity(3), iter_stack: vec![node] }
}
}
impl<'a> Iterator for SyntaxNodeWithDbIterator<'a> {
type Item = PrimitiveToken;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(item) = self.buffer.pop() {
return Some(item);
}
let node = self.iter_stack.pop()?;
if node.kind(self.db).is_terminal() {
token_from_syntax_node(node, self.db, &mut self.buffer);
} else {
self.iter_stack.extend(node.get_children(self.db).iter().rev());
}
}
}
}
fn token_from_syntax_node(
node: &SyntaxNode<'_>,
db: &dyn Database,
result: &mut Vec<PrimitiveToken>,
) {
let span_without_trivia = node.span_without_trivia(db);
let span_with_trivia = node.span(db);
let text = node.get_text(db);
let prefix_len = span_without_trivia.start - span_with_trivia.start;
let (prefix, rest) = text.split_at(prefix_len.as_u32() as usize);
let suffix_len = span_with_trivia.end - span_without_trivia.end;
let (content, suffix) = rest.split_at(rest.len() - suffix_len.as_u32() as usize);
if suffix_len > TextWidth::ZERO {
result.push(PrimitiveToken {
content: suffix.to_string(),
span: Some(PrimitiveSpan {
start: span_without_trivia.end.as_u32() as usize,
end: span_with_trivia.end.as_u32() as usize,
}),
});
}
if !content.is_empty() {
result.push(PrimitiveToken {
content: content.to_string(),
span: Some(PrimitiveSpan {
start: span_without_trivia.start.as_u32() as usize,
end: span_without_trivia.end.as_u32() as usize,
}),
});
}
if prefix_len > TextWidth::ZERO {
result.push(PrimitiveToken {
content: prefix.to_string(),
span: Some(PrimitiveSpan {
start: span_with_trivia.start.as_u32() as usize,
end: span_without_trivia.start.as_u32() as usize,
}),
});
}
}