#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SourceId(u32);
impl SourceId {
#[must_use]
pub const fn new(value: u32) -> Self {
Self(value)
}
#[must_use]
pub const fn get(self) -> u32 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct SourceSpan {
source_id: SourceId,
start: usize,
end: usize,
}
impl SourceSpan {
pub(crate) const fn new(source_id: SourceId, start: usize, end: usize) -> Self {
Self { source_id, start, end }
}
#[must_use]
pub const fn source_id(self) -> SourceId {
self.source_id
}
#[must_use]
pub const fn start(self) -> usize {
self.start
}
#[must_use]
pub const fn end(self) -> usize {
self.end
}
#[must_use]
pub const fn len(self) -> usize {
self.end - self.start
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.start == self.end
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LineColumn {
line: usize,
column: usize,
}
impl LineColumn {
#[must_use]
pub const fn line(self) -> usize {
self.line
}
#[must_use]
pub const fn column(self) -> usize {
self.column
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SourceText {
id: SourceId,
text: String,
line_starts: Vec<usize>,
}
impl SourceText {
pub(crate) fn new(id: SourceId, text: String) -> Self {
let mut line_starts = vec![0];
line_starts.extend(text.match_indices('\n').map(|(offset, _)| offset + 1));
Self { id, text, line_starts }
}
#[must_use]
pub const fn id(&self) -> SourceId {
self.id
}
#[must_use]
pub fn text(&self) -> &str {
&self.text
}
#[must_use]
pub fn slice(&self, span: SourceSpan) -> Option<&str> {
if span.source_id != self.id || span.start > span.end {
return None;
}
self.text.get(span.start..span.end)
}
#[must_use]
pub fn location(&self, offset: usize) -> Option<LineColumn> {
if offset > self.text.len() || !self.text.is_char_boundary(offset) {
return None;
}
let following = self.line_starts.partition_point(|start| *start <= offset);
let line_index = following.saturating_sub(1);
let line_start = *self.line_starts.get(line_index)?;
let column = self.text.get(line_start..offset)?.chars().count() + 1;
Some(LineColumn {
line: line_index + 1,
column,
})
}
}