142 lines
3.5 KiB
Rust
142 lines
3.5 KiB
Rust
use std::path::{Path, PathBuf};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Span {
|
|
pub file: usize,
|
|
pub start: usize,
|
|
pub end: usize,
|
|
}
|
|
|
|
pub struct Spanned<T> {
|
|
pub inner: T,
|
|
pub span: Span,
|
|
}
|
|
|
|
impl<T> std::ops::Deref for Spanned<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.inner
|
|
}
|
|
}
|
|
|
|
impl<T> std::ops::DerefMut for Spanned<T> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.inner
|
|
}
|
|
}
|
|
|
|
pub struct CompilerMsg {
|
|
pub spans: Vec<Span>,
|
|
pub msg: String,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct CompilerOutput {
|
|
pub errors: Vec<CompilerMsg>,
|
|
pub files: Vec<PathBuf>,
|
|
}
|
|
|
|
impl CompilerOutput {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
pub fn error(&mut self, msg: impl Into<CompilerMsg>) {
|
|
self.errors.push(msg.into());
|
|
}
|
|
pub fn write(&self, w: &mut impl std::io::Write) {
|
|
let files: Vec<_> = self
|
|
.files
|
|
.iter()
|
|
.map(|path| std::fs::read_to_string(path).unwrap())
|
|
.collect();
|
|
for error in &self.errors {
|
|
writeln!(w, "Error: {}", error.msg).unwrap();
|
|
for span in &error.spans {
|
|
span.write(w, &files[span.file]).unwrap();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Span {
|
|
pub fn write(&self, w: &mut impl std::io::Write, text: &str) -> std::io::Result<()> {
|
|
let mut line_start = 0;
|
|
let mut found = false;
|
|
let mut line = 1;
|
|
let mut spans = Vec::new();
|
|
for (i, c) in text.char_indices() {
|
|
if i == self.start {
|
|
found = true;
|
|
}
|
|
if i == self.end {
|
|
found = true;
|
|
}
|
|
if c == '\n' {
|
|
if found {
|
|
spans.push((line, line_start..i));
|
|
}
|
|
line_start = i + 1;
|
|
line += 1;
|
|
found = false;
|
|
}
|
|
}
|
|
let underline = "\x1b[4:3m";
|
|
let underline_color = "\x1b[58;5;1m";
|
|
let end = "\x1b[0m";
|
|
if let [(line, range)] = &spans[..] {
|
|
writeln!(
|
|
w,
|
|
" {line:3} | {}{underline}{underline_color}{}{end}{}",
|
|
&text[range.start..self.start],
|
|
&text[self.start..=self.end],
|
|
&text[(self.end + 1)..range.end]
|
|
)?;
|
|
} else if let [(sline, srange), (eline, erange)] = &spans[..] {
|
|
writeln!(
|
|
w,
|
|
" {sline:3} | {}{underline}{underline_color}{}{end}",
|
|
&text[srange.start..self.start],
|
|
&text[self.start..=srange.end - 1],
|
|
)?;
|
|
if *eline != *sline + 1 {
|
|
writeln!(w, " ...")?;
|
|
}
|
|
writeln!(
|
|
w,
|
|
" {eline:3} | {underline}{underline_color}{}{end}{}",
|
|
&text[erange.start..=self.end],
|
|
&text[(self.end + 1)..=erange.end - 1],
|
|
)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl From<String> for CompilerMsg {
|
|
fn from(msg: String) -> Self {
|
|
Self {
|
|
spans: Vec::new(),
|
|
msg,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&str> for CompilerMsg {
|
|
fn from(msg: &str) -> Self {
|
|
Self {
|
|
spans: Vec::new(),
|
|
msg: msg.to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: Into<String>> From<(S, Span)> for CompilerMsg {
|
|
fn from((msg, span): (S, Span)) -> Self {
|
|
Self {
|
|
spans: vec![span],
|
|
msg: msg.into(),
|
|
}
|
|
}
|
|
}
|