use crate::*;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Span {
pub(crate) page: Rc<Page>,
pub(crate) start: Place,
pub(crate) end: Place,
}
impl Span {
pub fn new(page: Rc<Page>, range: Range<usize>) -> Span {
let start = Place::new(&page, range.start);
let end = Place::new(&page, range.end);
Self { page, start, end }
}
pub fn page(&self) -> Rc<Page> {
Rc::clone(&self.page)
}
pub fn start(&self) -> Place {
self.start
}
pub fn end(&self) -> Place {
self.end
}
pub fn trim(&self) -> Span {
let page = Rc::clone(&self.page);
let mut start = self.start.index();
for character in self.slice().chars() {
if character.is_whitespace() { start += 1; }
else { break; }
}
let mut end = self.end.index();
for character in self.slice().chars().rev() {
if character.is_whitespace() && end >= start { end -= 1; }
else { break; }
}
let range = start ..end;
Span::new(page, range)
}
pub fn lines(&self) -> Vec<Span> {
let slice = self.slice();
let mut index = self.start.index();
let mut spans = vec![];
for slice_line in slice.lines() {
let start = index;
let length = slice_line.len();
index += length;
let end = index;
let span = Span::new(Rc::clone(&self.page()), start .. end);
spans.push(span);
}
spans
}
pub fn reassign_end(&mut self, end: usize) {
self.end = Place::new(&self.page(), end);
}
pub fn slice(&self) -> &str {
let string: &str = self.page.deref();
let slice = &string[self.range()];
slice
}
pub fn string(&self) -> &str {
&self.page.deref()
}
pub fn range(&self) -> Range<usize> {
self.start.index .. self.end.index
}
pub fn indentation_change(&self) -> i8 {
(self.end.indentation as i8) - (self.start.indentation as i8)
}
pub fn line_change(&self) -> i8 {
(self.end.line as i8) - (self.start.line as i8)
}
pub(crate) fn write_with_color(&self, formatter: &mut Formatter, color: Color) -> std::fmt::Result {
let mut stdout = stdout();
if self.slice().len() == 0 { return Ok(()); }
let string: &str = self.page.deref();
let range = self.range();
let line_range = self.start.line ..= self.end.line;
struct Line {
pub(crate) line_number: usize,
pub(crate) places: Vec::<Place>
}
let ref page = Rc::clone(&self.page);
let mut lines = Vec::<Line>::default();
for line_number in line_range.clone() {
let places = Vec::default();
let line = Line { line_number, places };
lines.push(line);
}
for (index, character) in string.char_indices() {
if character == '\n' { continue; }
let place = Place::new(page, index);
for line in lines.iter_mut() {
let line_number = line.line_number;
if line_range.contains(&line_number) {
line.places.push(place.clone());
}
}
}
for line in lines {
let line_number = line.line_number;
let _ = stdout.queue(SetForegroundColor(Color::Blue));
let _ = stdout.queue(SetAttribute(Attribute::Bold));
let _ = stdout.queue(SetAttribute(Attribute::Dim));
if let Some(path) = &self.page.path_maybe {
let path = path.display();
write!(formatter, "\n{path}")?;
}
let _ = write!(formatter, "\n{line_number} | ");
let _ = stdout.queue(SetAttribute(Attribute::Reset));
let places = line.places;
for place in places {
let place_line_number = place.line;
let character = place.character;
if range.contains(&place.index) {
let _ = stdout.queue(SetForegroundColor(color));
let _ = stdout.queue(SetAttribute(Attribute::Bold));
if character.is_whitespace() {
let _ = stdout.queue(SetAttribute(Attribute::Underlined));
}
}
if line_number == place_line_number {
write!(formatter, "{character}")?;
}
let _ = stdout.queue(SetAttribute(Attribute::Reset));
}
}
Ok(())
}
}
impl Display for Span {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let slice = self.slice();
write!(f, "{slice}")
}
}
impl Debug for Span {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let slice = self.slice();
let range = self.start.index ..= self.end.index;
write!(f, "[ {slice:?}, {range:?} ]")
}
}
impl Ord for Span {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let start_ordering = self.start.index.cmp(&other.start.index);
if let std::cmp::Ordering::Equal = start_ordering {
self.end.index.cmp(&other.end.index)
}
else {
start_ordering
}
}
}
impl PartialOrd for Span {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(&other))
}
}