use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub fn new(start: usize, end: usize) -> Self {
Span { start, end }
}
pub const DUMMY: Span = Span { start: 0, end: 0 };
pub fn to(self, other: Span) -> Span {
Span::new(self.start.min(other.start), self.end.max(other.end))
}
pub fn len(&self) -> usize {
self.end.saturating_sub(self.start)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct LineCol {
pub line: usize,
pub col: usize,
}
#[derive(Debug, Clone)]
pub struct SourceFile {
pub name: String,
pub text: String,
line_starts: Vec<usize>,
}
impl SourceFile {
pub fn new(name: impl Into<String>, text: impl Into<String>) -> Self {
let text = text.into();
let mut line_starts = vec![0];
for (i, b) in text.bytes().enumerate() {
if b == b'\n' {
line_starts.push(i + 1);
}
}
SourceFile {
name: name.into(),
text,
line_starts,
}
}
pub fn line_col(&self, offset: usize) -> LineCol {
let offset = offset.min(self.text.len());
let line_idx = match self.line_starts.binary_search(&offset) {
Ok(i) => i,
Err(i) => i - 1,
};
let line_start = self.line_starts[line_idx];
let col = self.text[line_start..offset].chars().count() + 1;
LineCol {
line: line_idx + 1,
col,
}
}
pub fn line_text(&self, line: usize) -> &str {
let idx = line - 1;
let start = self.line_starts[idx];
let end = self
.line_starts
.get(idx + 1)
.copied()
.unwrap_or(self.text.len());
self.text[start..end].trim_end_matches(['\n', '\r'])
}
pub fn line_count(&self) -> usize {
self.line_starts.len()
}
pub fn snippet(&self, span: Span) -> &str {
&self.text[span.start.min(self.text.len())..span.end.min(self.text.len())]
}
}