use sandpit::{Gc, Mutator, Trace, TraceLeaf};
use std::{io, fs};
#[derive(Clone, Debug, PartialEq)]
pub struct PackedSpans {
spans: Vec<(Span, usize)>,
}
#[derive(Trace, Clone)]
pub struct GcPackedSpans<'gc> {
spans: Gc<'gc, [(Span, usize)]>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Spanned<T> {
pub item: T,
pub span: Span,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, TraceLeaf)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
}
pub struct SpanSnippet {
pub line: usize,
pub _line_count: usize,
pub start: usize,
pub end: usize,
pub source_line: String
}
pub fn retrieve_span_snippet(path: &String, span: Span) -> io::Result<SpanSnippet> {
let source = fs::read_to_string(path)?;
let mut offset = 0;
let mut line_number = 1;
for line in source.lines() {
let line_len = line.len() + 1; let line_start = offset;
let line_end = offset + line_len;
if span.start >= line_start && span.start < line_end {
let col_start = span.start - line_start;
let col_end = (span.end.min(line_end)) - line_start;
let line_count = source[span.start..span.end]
.chars()
.filter(|&c| c == '\n')
.count() + 1;
return Ok(SpanSnippet {
line: line_number,
_line_count: line_count,
start: col_start,
end: col_end,
source_line: line.to_string(),
});
}
offset = line_end;
line_number += 1;
}
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"span out of bounds",
))
}
impl<'gc> GcPackedSpans<'gc> {
pub fn get(&self, i: usize) -> Option<&Span> {
for k in 0..self.spans.len() {
let (span, span_start) = &self.spans[k];
if i < *span_start {
continue;
}
if k + 1 == self.spans.len() {
return Some(span);
}
let span_end = &self.spans[k + 1].1;
if i >= *span_start && i < *span_end {
return Some(span);
}
}
None
}
}
impl<T> Spanned<T> {
pub fn new(item: T, span: Span) -> Self {
Self { item, span }
}
pub fn get_span(&self) -> Span {
self.span
}
pub fn map<F, B>(self, f: F) -> Spanned<B>
where
F: Fn(T) -> B,
{
let span = self.span;
let item = f(self.item);
Spanned { item, span }
}
}
impl Default for PackedSpans {
fn default() -> Self {
Self::new()
}
}
impl PackedSpans {
pub fn new() -> Self {
Self { spans: vec![] }
}
pub fn push(&mut self, span: Span, item_number: usize) {
if let Some(prev_span) = self.spans.last() {
if prev_span.0 != span {
self.spans.push((span, item_number))
}
} else {
self.spans.push((span, item_number))
}
}
pub fn into_gc<'gc>(&self, mu: &'gc Mutator) -> GcPackedSpans<'gc> {
GcPackedSpans {
spans: mu.alloc_array_from_slice(self.spans.as_slice()),
}
}
pub fn _is_empty(&self) -> bool {
self.spans.is_empty()
}
pub fn get(&self, i: usize) -> Option<&Span> {
for k in 0..self.spans.len() {
let (span, span_start) = &self.spans[k];
if i < *span_start {
continue;
}
if k + 1 == self.spans.len() {
return Some(span);
}
let span_end = &self.spans[k + 1].1;
if i >= *span_start && i < *span_end {
return Some(span);
}
}
None
}
pub fn remove(&mut self, i: usize) {
let mut remove_span = None;
for k in 0..self.spans.len() {
let (_, start) = &mut self.spans[k];
if i < *start {
*start -= 1;
}
let start = *start;
if k + 1 == self.spans.len() {
continue;
}
let (_, span_end) = self.spans[k + 1];
let adjusted_span_end = if i < span_end { span_end - 1 } else { span_end };
if start == adjusted_span_end {
remove_span = Some(k);
}
}
if let Some(i) = remove_span {
self.spans.remove(i);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let ps = PackedSpans::new();
assert_eq!(ps.get(0), None);
}
#[test]
fn single_span() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 1 }, 1);
assert_eq!(ps.get(0), None);
assert_eq!(ps.get(1), Some(&Span { start: 0, end: 1 }));
assert_eq!(ps.get(2), Some(&Span { start: 0, end: 1 }));
}
#[test]
fn multiple_spans_close_together() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 1);
ps.push(Span { start: 1, end: 1 }, 2);
ps.push(Span { start: 2, end: 2 }, 3);
assert_eq!(ps.get(0), None);
assert_eq!(ps.get(1), Some(&Span { start: 0, end: 0 }));
assert_eq!(ps.get(2), Some(&Span { start: 1, end: 1 }));
assert_eq!(ps.get(3), Some(&Span { start: 2, end: 2 }));
assert_eq!(ps.get(4), Some(&Span { start: 2, end: 2 }));
}
#[test]
fn multiple_spans_spread_out() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 10);
ps.push(Span { start: 1, end: 1 }, 20);
ps.push(Span { start: 2, end: 2 }, 30);
assert_eq!(ps.get(0), None);
assert_eq!(ps.get(5), None);
assert_eq!(ps.get(9), None);
assert_eq!(ps.get(10), Some(&Span { start: 0, end: 0 }));
assert_eq!(ps.get(15), Some(&Span { start: 0, end: 0 }));
assert_eq!(ps.get(19), Some(&Span { start: 0, end: 0 }));
assert_eq!(ps.get(20), Some(&Span { start: 1, end: 1 }));
assert_eq!(ps.get(25), Some(&Span { start: 1, end: 1 }));
assert_eq!(ps.get(29), Some(&Span { start: 1, end: 1 }));
assert_eq!(ps.get(30), Some(&Span { start: 2, end: 2 }));
assert_eq!(ps.get(35), Some(&Span { start: 2, end: 2 }));
assert_eq!(ps.get(100), Some(&Span { start: 2, end: 2 }));
}
#[test]
fn push_the_same_span() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 10);
ps.push(Span { start: 0, end: 0 }, 11);
ps.push(Span { start: 0, end: 0 }, 12);
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 0, end: 0 }, 10));
}
#[test]
fn removing_span() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 0);
ps.remove(1);
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 0, end: 0 }, 0));
}
#[test]
fn removing_sandwhiched_span() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 0);
ps.push(Span { start: 1, end: 1 }, 1);
ps.push(Span { start: 2, end: 2 }, 2);
ps.remove(1);
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 2, end: 2 }, 1));
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 0, end: 0 }, 0));
}
#[test]
fn removing_past_span_end() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 0);
ps.remove(10);
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 0, end: 0 }, 0));
}
#[test]
fn removing_before_span_start() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 1);
ps.remove(0);
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 0, end: 0 }, 0));
}
#[test]
fn removing_span_completely() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 0);
ps.push(Span { start: 1, end: 1 }, 1);
ps.push(Span { start: 2, end: 2 }, 2);
ps.push(Span { start: 3, end: 3 }, 3);
ps.remove(1);
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 3, end: 3 }, 2));
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 2, end: 2 }, 1));
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 0, end: 0 }, 0));
}
#[test]
fn removing_start_of_span() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 10);
ps.push(Span { start: 1, end: 1 }, 20);
ps.remove(10);
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 1, end: 1 }, 19));
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 0, end: 0 }, 10));
}
#[test]
fn removing_start_of_lone_span() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 10);
ps.remove(10);
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 0, end: 0 }, 10));
}
#[test]
fn removing_far_past_adjacent_spans_should_not_collapse() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 0 }, 6);
ps.push(Span { start: 1, end: 1 }, 7);
ps.remove(9);
assert_eq!(ps.spans.len(), 2);
assert_eq!(ps.spans[0], (Span { start: 0, end: 0 }, 6));
assert_eq!(ps.spans[1], (Span { start: 1, end: 1 }, 7));
}
#[test]
fn push_wrapping_span() {
let mut ps = PackedSpans::new();
ps.push(Span { start: 0, end: 10 }, 0);
ps.push(Span { start: 5, end: 8 }, 1);
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 5, end: 8 }, 1));
assert_eq!(ps.spans.pop().unwrap(), (Span { start: 0, end: 10 }, 0));
}
}