mod indexing;
#[cfg(test)]
mod test;
use crate::{location::Location, span::Span};
pub use indexing::SourceIndex;
use indexing::{IndexArray, IndexArrayBuilder, IndexArrayIter};
use std::{
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
ops::Index,
sync::Arc,
};
use unicode_segmentation::UnicodeSegmentation;
#[doc(hidden)]
pub fn count_grapheme_clusters(input: &str) -> usize {
input.graphemes(true).count()
}
#[derive(Debug)]
struct SourceInner {
name: Box<str>,
contents: Box<str>,
segments: IndexArray,
newlines: IndexArray,
}
#[derive(Debug, Clone)]
pub struct Source {
inner: Arc<SourceInner>,
}
impl Source {
pub fn new<S0, S1>(name: S0, contents: S1) -> Self
where
S0: Into<Box<str>>,
S1: Into<Box<str>>,
{
let name = name.into();
let contents = contents.into();
let mut segments = IndexArrayBuilder::new();
let mut newlines = IndexArrayBuilder::new();
for (idx, grapheme) in contents.grapheme_indices(true) {
if grapheme == "\n" {
newlines.push(segments.len());
}
segments.push(idx);
}
segments.push(contents.len());
let segments = segments.into();
let newlines = newlines.into();
let inner = SourceInner { name, contents, segments, newlines };
Self { inner: Arc::new(inner) }
}
pub fn name(&self) -> &str {
&self.inner.name
}
pub fn len(&self) -> usize {
self.inner.segments.len() - 1
}
pub fn contents(&self) -> &str {
&self.inner.contents
}
pub fn seg_byte_indices(&self) -> SegmentByteIndices {
SegmentByteIndices { inner: self.inner.segments.iter() }
}
pub fn newline_indices(&self) -> NewlineIndices {
NewlineIndices { inner: self.inner.segments.iter() }
}
pub(super) fn line(&self, position: usize) -> usize {
match self.inner.newlines.binary_search(position) {
Ok(n) | Err(n) => n,
}
}
pub(super) fn line_start(&self, line: usize) -> usize {
if line == 0 {
0
} else {
self.inner.newlines.index(line - 1) + 1
}
}
pub(super) fn try_line_start(&self, line: usize) -> Option<usize> {
if line == 0 {
Some(0)
} else {
self.inner.newlines.get(line - 1).map(|position| position + 1)
}
}
pub fn get<I>(&self, indexer: I) -> Option<&I::Output>
where
I: SourceIndex,
{
indexer.get(self)
}
pub fn full_span(&self) -> Span {
let start = Location::new_unchecked(self.clone(), 0);
Span::new_unchecked(start, self.len())
}
}
impl<I> Index<I> for Source
where
I: SourceIndex,
{
type Output = I::Output;
fn index(&self, indexer: I) -> &Self::Output {
indexer.index(self)
}
}
impl PartialEq for Source {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
}
impl Eq for Source {}
impl PartialOrd for Source {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Source {
fn cmp(&self, other: &Self) -> Ordering {
(&*self.inner as *const SourceInner).cmp(&(&*other.inner as *const _))
}
}
impl Hash for Source {
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
(&*self.inner as *const SourceInner).hash(hasher)
}
}
impl fmt::Display for Source {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
fmtr.write_str(self.name())
}
}
#[derive(Debug)]
pub struct SegmentByteIndices<'src> {
inner: IndexArrayIter<'src>,
}
impl<'src> Iterator for SegmentByteIndices<'src> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.inner.len();
(len, Some(len))
}
}
impl<'src> DoubleEndedIterator for SegmentByteIndices<'src> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}
impl<'array> ExactSizeIterator for SegmentByteIndices<'array> {}
#[derive(Debug)]
pub struct NewlineIndices<'src> {
inner: IndexArrayIter<'src>,
}
impl<'src> Iterator for NewlineIndices<'src> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.inner.len();
(len, Some(len))
}
}
impl<'src> DoubleEndedIterator for NewlineIndices<'src> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}
impl<'array> ExactSizeIterator for NewlineIndices<'array> {}