use crate::{
syntax_node::GreenNode, ParserError, SmolStr, SyntaxKind, SyntaxTreeBuilder, TextRange,
TextSize, TreeSink,
};
use rslint_lexer::Token;
use std::mem;
#[derive(Debug)]
pub struct LossyTreeSink<'a> {
text: &'a str,
tokens: &'a [Token],
text_pos: TextSize,
token_pos: usize,
state: State,
inner: SyntaxTreeBuilder,
errors: Vec<ParserError>,
}
#[derive(Debug, Clone, Copy)]
enum State {
PendingStart,
Normal,
PendingFinish,
}
impl<'a> TreeSink for LossyTreeSink<'a> {
fn consume_multiple_tokens(&mut self, amount: u8, kind: SyntaxKind) {
match mem::replace(&mut self.state, State::Normal) {
State::PendingStart => unreachable!(),
State::PendingFinish => self.inner.finish_node(),
State::Normal => (),
}
self.eat_trivias();
let len = TextSize::from(
self.tokens[self.token_pos..amount as usize]
.iter()
.map(|x| x.len)
.sum::<usize>() as u32,
);
let range = TextRange::at(self.text_pos, len);
let text: SmolStr = self.text[range].into();
self.text_pos += len;
self.token_pos += amount as usize;
self.inner.token(kind, text);
}
fn token(&mut self, kind: SyntaxKind) {
match mem::replace(&mut self.state, State::Normal) {
State::PendingStart => unreachable!(),
State::PendingFinish => self.inner.finish_node(),
State::Normal => (),
}
self.eat_trivias();
let len = TextSize::from(self.tokens[self.token_pos].len as u32);
self.do_token(kind, len, false);
}
fn start_node(&mut self, kind: SyntaxKind) {
match mem::replace(&mut self.state, State::Normal) {
State::PendingStart => {
self.inner.start_node(kind);
return;
}
State::PendingFinish => self.inner.finish_node(),
State::Normal => (),
}
let n_trivias = self.tokens[self.token_pos..]
.iter()
.take_while(|it| it.kind.is_trivia())
.count();
self.eat_n_trivias(n_trivias);
self.inner.start_node(kind);
}
fn finish_node(&mut self) {
match mem::replace(&mut self.state, State::PendingFinish) {
State::PendingStart => unreachable!(),
State::PendingFinish => self.inner.finish_node(),
State::Normal => (),
}
}
fn errors(&mut self, errors: Vec<ParserError>) {
self.errors = errors;
}
}
impl<'a> LossyTreeSink<'a> {
pub fn new(text: &'a str, tokens: &'a [Token]) -> Self {
Self {
text,
tokens,
text_pos: 0.into(),
token_pos: 0,
state: State::PendingStart,
inner: SyntaxTreeBuilder::default(),
errors: vec![],
}
}
pub fn with_offset(text: &'a str, tokens: &'a [Token], token_start: usize) -> Self {
let mut len = 0;
for (idx, tok) in tokens.iter().enumerate() {
if len == token_start {
return Self {
text,
tokens,
text_pos: (len as u32).into(),
token_pos: idx,
state: State::PendingStart,
inner: SyntaxTreeBuilder::default(),
errors: vec![],
};
}
len += tok.len;
}
panic!("Token start does not line up to a token or is out of bounds")
}
pub fn finish(mut self) -> (GreenNode, Vec<ParserError>) {
match mem::replace(&mut self.state, State::Normal) {
State::PendingFinish => {
self.eat_trivias();
self.inner.finish_node()
}
State::PendingStart | State::Normal => unreachable!(),
}
(self.inner.finish(), self.errors)
}
fn eat_trivias(&mut self) {
while let Some(&token) = self.tokens.get(self.token_pos) {
if !token.kind.is_trivia() {
break;
}
self.do_token(token.kind, TextSize::from(token.len as u32), true);
}
}
fn eat_n_trivias(&mut self, n: usize) {
for _ in 0..n {
let token = self.tokens[self.token_pos];
assert!(token.kind.is_trivia());
self.do_token(token.kind, TextSize::from(token.len as u32), true);
}
}
fn do_token(&mut self, kind: SyntaxKind, len: TextSize, skip: bool) {
let range = TextRange::at(self.text_pos, len);
let text: SmolStr = self.text[range].into();
self.text_pos += len;
self.token_pos += 1;
if !skip {
self.inner.token(kind, text);
}
}
}