#[cfg(feature = "serde_borsh")]
use borsh::{
maybestd::io::Result as BorshResult, maybestd::io::Write as BorshWrite, BorshDeserialize,
BorshSerialize,
};
use std::borrow::Borrow;
use std::fmt;
use std::fmt::Write;
use std::rc::Rc;
pub type Pos = usize;
#[derive(Clone, Debug)]
pub struct FilePos {
pub filename: Rc<String>,
pub offset: usize, pub line: usize,
pub column: usize,
}
impl FilePos {
pub fn is_valid(&self) -> bool {
self.line > 0
}
pub fn null() -> FilePos {
FilePos {
filename: Rc::new("[null_file]".to_owned()),
line: 0,
offset: 0,
column: 0,
}
}
}
impl fmt::Display for FilePos {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::clone(&*self.filename);
if self.is_valid() {
if s != "" {
s.push(':');
}
s.push_str(&self.line.to_string());
}
if self.column != 0 {
write!(&mut s, ":{}", self.column).unwrap();
}
if s.is_empty() {
s.push('-');
}
f.write_str(&s)
}
}
#[derive(Debug)]
pub struct File {
name: Rc<String>,
base: usize,
size: usize,
lines: Vec<usize>,
}
impl File {
pub fn new(name: String) -> File {
File {
name: Rc::new(name),
base: 0,
size: 0,
lines: vec![0],
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn base(&self) -> usize {
self.base
}
pub fn size(&self) -> usize {
self.size
}
pub fn line_count(&self) -> usize {
self.lines.len()
}
pub fn add_line(&mut self, offset: usize) {
let i = self.line_count();
if (i == 0 || self.lines[i - 1] < offset) && offset < self.size {
self.lines.push(offset);
}
}
pub fn merge_line(&mut self, line: usize) {
if line < 1 {
panic!("illegal line number (line numbering starts at 1)");
}
if line >= self.line_count() {
panic!("illegal line number");
}
let lines = &self.lines;
self.lines = lines
.into_iter()
.enumerate()
.filter(|&(i, _)| i != line)
.map(|(_, l)| *l)
.collect();
}
pub fn set_lines(&mut self, lines: Vec<usize>) -> bool {
let size = self.size;
for (i, &offset) in self.lines.iter().enumerate() {
if (i == 0 && size <= offset) || offset < lines[i - 1] {
return false;
}
}
self.lines = lines;
true
}
pub fn set_lines_for_content(&mut self, content: &mut std::str::Chars) {
let (mut new_line, mut line) = (true, 0);
for (offset, b) in content.enumerate() {
if new_line {
self.lines.push(line);
}
new_line = false;
if b == '\n' {
new_line = true;
line = offset + 1;
}
}
}
pub fn line_start(&self, line: usize) -> usize {
if line < 1 {
panic!("illegal line number (line numbering starts at 1)");
}
if line >= self.line_count() {
panic!("illegal line number");
}
self.base + self.lines[line - 1]
}
pub fn pos(&self, offset: usize) -> Pos {
if offset > self.size() {
panic!("illegal file offset")
}
self.base() + offset
}
pub fn position(&self, p: Pos) -> FilePos {
if p < self.base || p > self.base + self.size {
panic!("illegal Pos value");
}
let line_count = self.line_count();
let offset = p - self.base;
let line = match self
.lines
.iter()
.enumerate()
.find(|&(_, &line)| line > offset)
{
Some((i, _)) => i,
None => line_count,
};
let column = offset - self.lines[line - 1] + 1;
FilePos {
filename: self.name.clone(),
line: line,
offset: offset,
column: column,
}
}
}
#[cfg(feature = "serde_borsh")]
impl BorshSerialize for File {
#[inline]
fn serialize<W: BorshWrite>(&self, writer: &mut W) -> BorshResult<()> {
let name_str: &str = &*self.name;
name_str.serialize(writer)?;
self.base.serialize(writer)?;
self.size.serialize(writer)?;
self.lines.serialize(writer)
}
}
#[cfg(feature = "serde_borsh")]
impl BorshDeserialize for File {
#[inline]
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> BorshResult<Self> {
let name = String::deserialize_reader(reader)?;
let base = usize::deserialize_reader(reader)?;
let size = usize::deserialize_reader(reader)?;
let lines = Vec::<usize>::deserialize_reader(reader)?;
Ok(File {
name: Rc::new(name),
base,
size,
lines,
})
}
}
#[cfg_attr(feature = "serde_borsh", derive(BorshDeserialize, BorshSerialize))]
#[derive(Debug)]
pub struct FileSet {
base: usize,
files: Vec<File>,
}
impl FileSet {
pub fn new() -> FileSet {
FileSet {
base: 0,
files: vec![],
}
}
pub fn base(&self) -> usize {
self.base
}
pub fn iter(&self) -> FileSetIter {
FileSetIter { fs: self, cur: 0 }
}
pub fn file(&self, p: Pos) -> Option<&File> {
for f in self.files.iter() {
if f.base <= p && f.base + f.size >= p {
return Some(f.borrow());
}
}
None
}
pub fn position(&self, p: Pos) -> Option<FilePos> {
self.file(p).map(|f| f.position(p))
}
pub fn index_file(&mut self, i: usize) -> Option<&mut File> {
if i >= self.files.len() {
None
} else {
Some(&mut self.files[i])
}
}
pub fn recent_file(&mut self) -> Option<&mut File> {
let c = self.files.len();
if c == 0 {
None
} else {
self.index_file(c - 1)
}
}
pub fn add_file(&mut self, name: String, base: Option<usize>, size: usize) -> &mut File {
let real_base = if let Some(b) = base { b } else { self.base };
if real_base < self.base {
panic!("illegal base");
}
let mut f = File::new(name);
f.base = real_base;
f.size = size;
let set_base = self.base + size + 1; if set_base < self.base {
panic!("token.Pos offset overflow (> 2G of source code in file set)");
}
self.base = set_base;
self.files.push(f);
self.recent_file().unwrap()
}
}
pub struct FileSetIter<'a> {
fs: &'a FileSet,
cur: usize,
}
impl<'a> Iterator for FileSetIter<'a> {
type Item = &'a File;
fn next(&mut self) -> Option<&'a File> {
if self.cur < self.fs.files.len() {
self.cur += 1;
Some(self.fs.files[self.cur - 1].borrow())
} else {
None
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_position() {
let p = FilePos {
filename: Rc::new("test.gs".to_owned()),
offset: 0,
line: 54321,
column: 8,
};
print!("this is the position: {} ", p);
let mut fs = FileSet::new();
let mut f = File::new("test.gs".to_owned());
f.size = 12345;
f.add_line(123);
f.add_line(133);
f.add_line(143);
print!("\nfile: {:?}", f);
f.merge_line(1);
print!("\nfile after merge: {:?}", f);
{
fs.add_file("testfile1.gs".to_owned(), None, 222);
fs.add_file("testfile2.gs".to_owned(), None, 222);
fs.add_file("testfile3.gs".to_owned(), None, 222);
print!("\nset {:?}", fs);
}
for f in fs.iter() {
print!("\nfiles in set: {:?}", f);
}
print!("\nfile at 100: {:?}", fs.file(100))
}
}