pub use super::*;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
pub trait FileData {
type Source: ?Sized + AsRef<str> + PartialEq;
type Name: ?Sized + fmt::Display + fmt::Debug + PartialEq;
fn source(&self) -> &Self::Source;
fn name(&self) -> &Self::Name;
}
pub struct File<T: FileData> {
pub span: Span,
pub(crate) source: T,
pub(crate) lines: Vec<Pos>,
}
impl<T: FileData> Deref for File<T> {
type Target = T;
fn deref(&self) -> &T {
&self.source
}
}
impl<T: FileData> File<T> {
pub fn find_line(&self, pos: Pos) -> usize {
assert!(pos >= self.span.low);
assert!(pos <= self.span.high);
match self.lines.binary_search(&pos) {
Ok(i) => i,
Err(i) => i - 1,
}
}
pub fn find_line_col(&self, pos: Pos) -> LineCol {
let line = self.find_line(pos);
let line_span = self.line_span(line);
let byte_col = pos - line_span.low;
let column = self.source_slice(line_span)[..byte_col as usize]
.chars()
.count();
LineCol { line, column }
}
pub fn source_slice(&self, span: Span) -> &str {
assert!(self.span.contains(span));
&self.source().as_ref()
[((span.low - self.span.low) as usize)..((span.high - self.span.low) as usize)]
}
pub fn line_span(&self, line: usize) -> Span {
assert!(line < self.lines.len());
Span {
low: self.lines[line],
high: *self.lines.get(line + 1).unwrap_or(&self.span.high),
}
}
pub fn source_line(&self, line: usize) -> &str {
self.source_slice(self.line_span(line))
.trim_end_matches(&['\n', '\r'][..])
}
pub fn num_lines(&self) -> usize {
self.lines.len()
}
}
impl<T: FileData> fmt::Debug for File<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "File({:?})", self.name())
}
}
impl<T: FileData> PartialEq for File<T> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self as *const _, other as *const _)
}
}
impl<T: FileData> Eq for File<T> {}
impl<T: FileData> Hash for File<T> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.span.hash(hasher);
}
}
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct LineCol {
pub line: usize,
pub column: usize,
}
#[derive(Eq, Debug)]
pub struct Loc<T: FileData> {
pub file: Arc<File<T>>,
pub position: LineCol,
}
impl<T: FileData> fmt::Display for Loc<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
f,
"{}:{}:{}",
self.file.name(),
self.position.line + 1,
self.position.column + 1
)
}
}
impl<T: FileData> Clone for Loc<T> {
fn clone(&self) -> Self {
Self {
file: Arc::clone(&self.file),
position: self.position,
}
}
}
impl<T: FileData> std::cmp::PartialEq for Loc<T> {
fn eq(&self, other: &Self) -> bool {
self.position == other.position && self.file == other.file
}
}
#[derive(Debug, Eq)]
pub struct SpanLoc<T: FileData> {
pub file: Arc<File<T>>,
pub begin: LineCol,
pub end: LineCol,
}
impl<T: FileData> Clone for SpanLoc<T> {
fn clone(&self) -> Self {
Self {
file: Arc::clone(&self.file),
begin: self.begin,
end: self.end,
}
}
}
impl<T: FileData> std::cmp::PartialEq for SpanLoc<T> {
fn eq(&self, other: &Self) -> bool {
self.begin == other.begin && self.end == other.end && self.file == other.file
}
}
impl<T: FileData> fmt::Display for SpanLoc<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
if self.begin == self.end {
write!(
f,
"{}:{}:{}",
self.file.name(),
self.begin.line + 1,
self.begin.column + 1
)
} else {
write!(
f,
"{}:{}:{}: {}:{}",
self.file.name(),
self.begin.line + 1,
self.begin.column + 1,
self.end.line + 1,
self.end.column + 1
)
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct BoxStr(Box<str>);
impl BoxStr {
pub const fn new(s: Box<str>) -> Self {
Self(s)
}
pub fn from_string(s: String) -> Self {
Self::new(s.into_boxed_str())
}
pub fn into_inner(self) -> Box<str> {
self.0
}
}
impl Deref for BoxStr {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl fmt::Display for BoxStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl AsRef<str> for BoxStr {
fn as_ref(&self) -> &str {
self
}
}
impl PartialEq for BoxStr {
fn eq(&self, other: &Self) -> bool {
let self_ptr: *const _ = &*(self.0);
let other_ptr: *const _ = &*(other.0);
std::ptr::eq(self_ptr, other_ptr)
}
}
impl Eq for BoxStr {}
impl PartialEq<str> for BoxStr {
fn eq(&self, other: &str) -> bool {
&*self.0 == other
}
}
#[derive(Debug)]
pub struct DefaultFileData {
name: BoxStr,
contents: BoxStr,
}
impl DefaultFileData {
pub fn new(name: String, contents: String) -> Self {
Self {
name: BoxStr(name.into_boxed_str()),
contents: BoxStr(contents.into_boxed_str()),
}
}
}
impl FileData for DefaultFileData {
type Source = BoxStr;
type Name = BoxStr;
fn source(&self) -> &Self::Source {
&self.contents
}
fn name(&self) -> &Self::Name {
&self.name
}
}