use ktrs_syntax::{Parse, SyntaxKind};
use super::binders::EdgeBinder;
use super::psi_builder::PsiBuilder;
use super::sink::TreeSink;
pub trait LazyReparse: Fn(&LazyLeaf<'_>, &mut TreeSink) -> bool {}
impl<F: Fn(&LazyLeaf<'_>, &mut TreeSink) -> bool> LazyReparse for F {}
pub struct LazyLeaf<'a> {
pub kind: SyntaxKind,
pub text: &'a str,
outer: &'a PsiBuilder,
start: usize,
end: usize,
}
impl LazyLeaf<'_> {
pub(crate) fn relexed_builder(&self) -> PsiBuilder {
let (start, end) = (self.start, self.end);
PsiBuilder::from_lexemes(self.text, &self.outer.lex_starts[start..=end], &self.outer.orig_types[start..end])
}
}
impl PsiBuilder {
pub fn get_tree_built(&mut self, lazy: &impl LazyReparse) -> Parse {
let mut sink = TreeSink::new();
self.build_tree_into(None, &mut sink, lazy);
sink.finish()
}
pub fn build_tree_into(&mut self, root_kind: Option<SyntaxKind>, sink: &mut TreeSink, lazy: &impl LazyReparse) {
assert!(!self.production.is_empty(), "Parser produced no markers");
self.balance_white_spaces();
let mut skipped_errors = std::mem::take(&mut self.skipped_errors);
self.duplicate_error_items(&mut skipped_errors);
self.bind(root_kind, &skipped_errors, sink, lazy);
self.skipped_errors = skipped_errors;
}
fn balance_white_spaces(&mut self) {
let mut last_index: i32 = 0;
let mut prev_index = self.production.get_lexeme_index_at(0);
let size = self.production.size().saturating_sub(1);
for i in 1..size {
let id = self.production.list[i];
let done = id < 0;
let item = self.production.marker(id);
assert!(done || item.is_error_item || item.is_done(), "Unbalanced tree: marker not done");
let binder = if item.is_error_item { EdgeBinder::DefaultRight } else { item.get_binder(done) };
let mut lexeme_index = item.get_lexeme_index(done);
if binder == EdgeBinder::DefaultLeft {
lexeme_index = self.shift_over_whitespace_forward(lexeme_index as usize) as i32;
self.production.marker_mut(id).set_lexeme_index(lexeme_index, done);
(last_index, prev_index) = (lexeme_index, lexeme_index);
continue;
}
let prev_production_lex_index = prev_index;
let mut ws_start_index = lexeme_index.max(last_index);
while ws_start_index > prev_production_lex_index
&& self.is_whitespace_or_comment(self.lex_types[ws_start_index as usize - 1])
{
ws_start_index -= 1;
}
if binder == EdgeBinder::DefaultRight {
self.production.marker_mut(id).set_lexeme_index(ws_start_index, done);
(last_index, prev_index) = (ws_start_index, ws_start_index);
continue;
}
let ws_end_index = self.shift_over_whitespace_forward(lexeme_index as usize) as i32;
if ws_start_index != ws_end_index {
debug_assert!(ws_start_index < ws_end_index);
let (start, end) = (ws_start_index as usize, ws_end_index as usize);
let at_end = ws_start_index == 0 || end == self.lexeme_count();
let getter = |i: usize| self.token_text(start + i);
let edge = binder.get_edge_position(&self.lex_types[start..end], at_end, &getter);
lexeme_index = ws_start_index + edge as i32;
self.production.marker_mut(id).set_lexeme_index(lexeme_index, done);
} else if lexeme_index < ws_start_index {
lexeme_index = ws_start_index;
self.production.marker_mut(id).set_lexeme_index(ws_start_index, done);
}
last_index = lexeme_index;
prev_index = lexeme_index;
}
}
fn duplicate_error_items(&self, skipped: &mut Vec<bool>) {
skipped.clear();
if !self.production.has_error_items() {
return;
}
skipped.resize(self.production.size(), false);
let mut last_error_index = -1;
for (i, &id) in self.production.list.iter().enumerate().skip(1) {
if id > 0 && self.production.marker(id).is_error_item {
let cur_token = self.production.marker(id).lexeme;
if cur_token != last_error_index {
last_error_index = cur_token;
} else {
skipped[i] = true;
}
}
}
}
fn bind(&self, root_kind: Option<SyntaxKind>, skipped_errors: &[bool], out: &mut TreeSink, lazy: &impl LazyReparse) {
let list = &self.production.list;
let root = self.production.marker(list[0]);
out.start_node(root_kind.unwrap_or_else(|| kind_of(root.kind)));
let mut depth = 1;
let mut lex_index = root.lexeme.max(0) as usize;
let mut i = 1;
while i < list.len() {
let id = list[i];
let item = self.production.marker(id);
if id < 0 {
lex_index = self.insert_leaves(lex_index, item.done_lexeme, out, lazy);
if id == -list[0] {
break;
}
out.finish_node();
depth -= 1;
} else if item.is_error_item {
if !skipped_errors[i] {
lex_index = self.insert_leaves(lex_index, item.lexeme, out, lazy);
out.errors.push(self.production.message(id).unwrap_or_default().to_owned());
out.start_node(SyntaxKind::ERROR_ELEMENT);
out.finish_node();
}
} else {
lex_index = self.insert_leaves(lex_index, item.lexeme, out, lazy);
if item.collapsed {
lex_index = self.collapse_leaves(item.lexeme, item.done_lexeme, kind_of(item.kind), out, lazy);
i = list[i..].iter().position(|&x| x == -id).map_or(list.len(), |p| i + p);
} else {
let kind = kind_of(item.kind);
if kind == SyntaxKind::ERROR_ELEMENT {
out.errors.push(self.production.message(id).expect("error marker without message").to_owned());
}
out.start_node(kind);
depth += 1;
}
}
i += 1;
}
for _ in 0..depth {
out.finish_node();
}
}
fn insert_leaves(&self, cur_token: usize, last_idx: i32, out: &mut TreeSink, lazy: &impl LazyReparse) -> usize {
let last_idx = (last_idx.max(0) as usize).min(self.lexeme_count());
if cur_token >= last_idx {
return cur_token;
}
let kinds = &self.lex_types[cur_token..last_idx];
let starts = &self.lex_starts[cur_token..=last_idx];
for (i, (&kind, bounds)) in kinds.iter().zip(starts.windows(2)).enumerate() {
if bounds[0] < bounds[1] {
let text = &self.text[bounds[0] as usize..bounds[1] as usize];
self.create_leaf(kind, cur_token + i, cur_token + i + 1, text, out, lazy);
}
}
last_idx
}
fn collapse_leaves(&self, start: i32, end: i32, kind: SyntaxKind, out: &mut TreeSink, lazy: &impl LazyReparse) -> usize {
let (start, end) = (start as usize, end as usize);
let text = &self.text[self.lex_starts[start] as usize..self.lex_starts[end] as usize];
self.create_leaf(kind, start, end, text, out, lazy);
end
}
fn create_leaf(
&self,
kind: SyntaxKind,
start: usize,
end: usize,
text: &str,
out: &mut TreeSink,
lazy: &impl LazyReparse,
) {
let leaf = LazyLeaf { kind, text, outer: self, start, end };
if !lazy(&leaf, out) {
out.token(kind, leaf.text);
}
}
pub(crate) fn token_text(&self, lexeme: usize) -> &str {
&self.text[self.lex_starts[lexeme] as usize..self.lex_starts[lexeme + 1] as usize]
}
}
fn kind_of(kind: Option<SyntaxKind>) -> SyntaxKind {
kind.expect("Unbalanced tree. Most probably caused by unbalanced markers.")
}