use std::cell::{RefCell, Cell, RefMut};
use std::rc::Rc;
use crate::ns::*;
const LINE_SKIP_THRESOLD: usize = 10;
const HIGHER_LINE_SKIP_THRESOLD: usize = 100;
const EXTRA_HIGHER_LINE_SKIP_THRESOLD: usize = 1_000;
pub struct CompilationUnit {
pub(crate) file_path: Option<String>,
pub(crate) text: String,
pub(crate) line_skips: RefCell<Vec<LineSkip>>,
pub(crate) line_skips_counter: Cell<usize>,
pub(crate) higher_line_skips: RefCell<Vec<HigherLineSkip>>,
pub(crate) higher_line_skips_counter: Cell<usize>,
pub(crate) extra_higher_line_skips: RefCell<Vec<HigherLineSkip>>,
pub(crate) extra_higher_line_skips_counter: Cell<usize>,
pub(crate) already_tokenized: Cell<bool>,
diagnostics: RefCell<Vec<Diagnostic>>,
pub(crate) error_count: Cell<u32>,
pub(crate) warning_count: Cell<u32>,
pub(crate) invalidated: Cell<bool>,
pub(crate) compiler_options: Rc<CompilerOptions>,
pub(crate) comments: RefCell<Vec<Rc<Comment>>>,
}
#[derive(Copy, Clone)]
pub(crate) struct LineSkip {
pub offset: usize,
pub line_number: usize,
}
#[derive(Copy, Clone)]
pub(crate) struct HigherLineSkip {
pub skip_index: usize,
pub offset: usize,
pub line_number: usize,
}
impl Default for CompilationUnit {
fn default() -> Self {
Self {
file_path: None,
text: "".into(),
line_skips: RefCell::new(vec![LineSkip { offset: 0, line_number: 1 }]),
line_skips_counter: Cell::new(0),
higher_line_skips: RefCell::new(vec![HigherLineSkip { skip_index: 0, offset: 0, line_number: 1 }]),
higher_line_skips_counter: Cell::new(0),
extra_higher_line_skips: RefCell::new(vec![HigherLineSkip { skip_index: 0, offset: 0, line_number: 1 }]),
extra_higher_line_skips_counter: Cell::new(0),
already_tokenized: Cell::new(false),
diagnostics: RefCell::new(vec![]),
invalidated: Cell::new(false),
error_count: Cell::new(0),
warning_count: Cell::new(0),
compiler_options: CompilerOptions::new(),
comments: RefCell::new(vec![]),
}
}
}
impl CompilationUnit {
pub fn new(file_path: Option<String>, text: String, compiler_options: &Rc<CompilerOptions>) -> Rc<Self> {
Rc::new(Self {
file_path,
text,
line_skips: RefCell::new(vec![LineSkip { offset: 0, line_number: 1 }]),
line_skips_counter: Cell::new(0),
higher_line_skips: RefCell::new(vec![HigherLineSkip { skip_index: 0, offset: 0, line_number: 1 }]),
higher_line_skips_counter: Cell::new(0),
extra_higher_line_skips: RefCell::new(vec![HigherLineSkip { skip_index: 0, offset: 0, line_number: 1 }]),
extra_higher_line_skips_counter: Cell::new(0),
already_tokenized: Cell::new(false),
diagnostics: RefCell::new(vec![]),
invalidated: Cell::new(false),
error_count: Cell::new(0),
warning_count: Cell::new(0),
compiler_options: compiler_options.clone(),
comments: RefCell::new(vec![]),
})
}
pub fn file_path(&self) -> Option<String> {
self.file_path.clone()
}
pub fn text(&self) -> &String {
&self.text
}
pub fn invalidated(&self) -> bool {
self.invalidated.get()
}
pub fn comments(&self) -> Vec<Rc<Comment>> {
let mut collection = vec![];
for c in self.comments.borrow().iter() {
collection.push(c.clone());
}
collection
}
pub fn comments_mut(&self) -> RefMut<Vec<Rc<Comment>>> {
self.comments.borrow_mut()
}
pub fn diagnostics(&self) -> Vec<Diagnostic> {
self.diagnostics.borrow().clone()
}
pub fn sort_diagnostics(&self) {
self.diagnostics.borrow_mut().sort();
}
pub fn add_diagnostic(&self, diagnostic: Diagnostic) {
if diagnostic.is_warning() {
self.warning_count.set(self.warning_count.get() + 1);
} else {
self.error_count.set(self.error_count.get() + 1);
self.invalidated.set(true);
}
self.diagnostics.borrow_mut().push(diagnostic);
}
pub fn error_count(&self) -> u32 {
self.error_count.get()
}
pub fn warning_count(&self) -> u32 {
self.warning_count.get()
}
pub(crate) fn push_line_skip(&self, line_number: usize, offset: usize) {
let counter = self.line_skips_counter.get();
if counter == LINE_SKIP_THRESOLD {
self.line_skips.borrow_mut().push(LineSkip { line_number, offset });
self.line_skips_counter.set(0);
} else {
self.line_skips_counter.set(counter + 1);
}
let counter = self.higher_line_skips_counter.get();
if counter == HIGHER_LINE_SKIP_THRESOLD {
self.higher_line_skips.borrow_mut().push(HigherLineSkip { skip_index: self.line_skips.borrow().len() - 1, line_number, offset });
self.higher_line_skips_counter.set(0);
} else {
self.higher_line_skips_counter.set(counter + 1);
}
let counter = self.extra_higher_line_skips_counter.get();
if counter == EXTRA_HIGHER_LINE_SKIP_THRESOLD {
self.extra_higher_line_skips.borrow_mut().push(HigherLineSkip { skip_index: self.higher_line_skips.borrow().len() - 1, line_number, offset });
self.extra_higher_line_skips_counter.set(0);
} else {
self.extra_higher_line_skips_counter.set(counter + 1);
}
}
pub fn get_line_number(&self, offset: usize) -> usize {
let mut last_skip = HigherLineSkip { skip_index: 0, offset: 0, line_number: 1 };
let skips = self.extra_higher_line_skips.borrow();
let mut skips = skips.iter();
while let Some(skip_1) = skips.next() {
if offset < skip_1.offset {
break;
}
last_skip = *skip_1;
}
let skips = self.higher_line_skips.borrow();
let mut skips = skips[last_skip.skip_index..].iter();
let mut last_skip = skips.next().unwrap();
while let Some(skip_1) = skips.next() {
if offset < skip_1.offset {
break;
}
last_skip = skip_1;
}
let skips = self.line_skips.borrow();
let mut skips = skips[last_skip.skip_index..].iter();
let mut last_skip = skips.next().unwrap();
while let Some(skip_1) = skips.next() {
if offset < skip_1.offset {
break;
}
last_skip = skip_1;
}
let mut current_line = last_skip.line_number;
let mut characters = CharacterReader::from(&self.text[last_skip.offset..]);
while last_skip.offset + characters.index() < offset {
let ch_1 = characters.next();
if let Some(ch_1) = ch_1 {
if CharacterValidator::is_line_terminator(ch_1) {
if ch_1 == '\r' && characters.peek_or_zero() == '\n' {
characters.next();
}
current_line += 1;
}
} else {
break;
}
}
current_line
}
pub fn get_line_offset(&self, line: usize) -> Option<usize> {
let mut last_skip = HigherLineSkip { skip_index: 0, offset: 0, line_number: 1 };
let skips = self.extra_higher_line_skips.borrow();
let mut skips = skips.iter();
while let Some(skip_1) = skips.next() {
if line < skip_1.line_number {
break;
}
last_skip = *skip_1;
}
let skips = self.higher_line_skips.borrow();
let mut skips = skips[last_skip.skip_index..].iter();
let mut last_skip = skips.next().unwrap();
while let Some(skip_1) = skips.next() {
if line < skip_1.line_number {
break;
}
last_skip = skip_1;
}
let skips = self.line_skips.borrow();
let mut skips = skips[last_skip.skip_index..].iter();
let mut last_skip = skips.next().unwrap();
while let Some(skip_1) = skips.next() {
if line < skip_1.line_number {
break;
}
last_skip = skip_1;
}
let mut current_line = last_skip.line_number;
let mut characters = CharacterReader::from(&self.text[last_skip.offset..]);
while current_line != line {
let ch_1 = characters.next();
if let Some(ch_1) = ch_1 {
if CharacterValidator::is_line_terminator(ch_1) {
if ch_1 == '\r' && characters.peek_or_zero() == '\n' {
characters.next();
}
current_line += 1;
}
} else {
return None;
}
}
Some(last_skip.offset + characters.index())
}
pub fn get_line_offset_from_offset(&self, offset: usize) -> usize {
let mut last_skip = HigherLineSkip { skip_index: 0, offset: 0, line_number: 1 };
let skips = self.extra_higher_line_skips.borrow();
let mut skips = skips.iter();
while let Some(skip_1) = skips.next() {
if offset < skip_1.offset {
break;
}
last_skip = *skip_1;
}
let skips = self.higher_line_skips.borrow();
let mut skips = skips[last_skip.skip_index..].iter();
let mut last_skip = skips.next().unwrap();
while let Some(skip_1) = skips.next() {
if offset < skip_1.offset {
break;
}
last_skip = skip_1;
}
let skips = self.line_skips.borrow();
let mut skips = skips[last_skip.skip_index..].iter();
let mut last_skip = skips.next().unwrap();
while let Some(skip_1) = skips.next() {
if offset < skip_1.offset {
break;
}
last_skip = skip_1;
}
let mut current_line_offset = last_skip.offset;
let mut characters = CharacterReader::from(&self.text[last_skip.offset..]);
while last_skip.offset + characters.index() < offset {
let ch_1 = characters.next();
if let Some(ch_1) = ch_1 {
if CharacterValidator::is_line_terminator(ch_1) {
if ch_1 == '\r' && characters.peek_or_zero() == '\n' {
characters.next();
}
current_line_offset = last_skip.offset + characters.index();
}
} else {
break;
}
}
current_line_offset
}
pub fn get_line_indent(&self, line: usize) -> usize {
let line_offset = self.get_line_offset(line).unwrap();
let indent = CharacterValidator::indent_count(&self.text[line_offset..]);
indent - line_offset
}
}