1mod location;23use std::path::{Path, PathBuf};45pub use location::*;67use crate::{error::Error, LocError, State};8910pub enum PathResolver {11 12 FileName,13 14 Absolute,15 16 Relative(PathBuf),17}1819impl PathResolver {20 pub fn resolve(&self, from: &Path) -> String {21 match self {22 Self::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),23 Self::Absolute => from.to_string_lossy().into_owned(),24 Self::Relative(base) => {25 if from.is_relative() {26 return from.to_string_lossy().into_owned();27 }28 pathdiff::diff_paths(from, base)29 .unwrap()30 .to_string_lossy()31 .into_owned()32 }33 }34 }35}363738pub trait TraceFormat {39 fn write_trace(40 &self,41 out: &mut dyn std::fmt::Write,42 s: &State,43 error: &LocError,44 ) -> Result<(), std::fmt::Error>;45}4647fn print_code_location(48 out: &mut impl std::fmt::Write,49 start: &CodeLocation,50 end: &CodeLocation,51) -> Result<(), std::fmt::Error> {52 if start.line == end.line {53 if start.column == end.column {54 write!(out, "{}:{}", start.line, end.column - 1)?;55 } else {56 write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;57 }58 } else {59 write!(60 out,61 "{}:{}-{}:{}",62 start.line,63 end.column.saturating_sub(1),64 start.line,65 end.column66 )?;67 }68 Ok(())69}707172pub struct CompactFormat {73 pub resolver: PathResolver,74 pub padding: usize,75}7677impl TraceFormat for CompactFormat {78 fn write_trace(79 &self,80 out: &mut dyn std::fmt::Write,81 s: &State,82 error: &LocError,83 ) -> Result<(), std::fmt::Error> {84 write!(out, "{}", error.error())?;85 if let Error::ImportSyntaxError {86 path,87 source_code,88 error,89 } = error.error()90 {91 writeln!(out)?;92 use std::fmt::Write;93 let mut n = self.resolver.resolve(path);94 let mut offset = error.location.offset;95 let is_eof = if offset >= source_code.len() {96 offset = source_code.len().saturating_sub(1);97 true98 } else {99 false100 };101 let mut location = offset_to_location(source_code, &[offset])102 .into_iter()103 .next()104 .unwrap();105 if is_eof {106 location.column += 1;107 }108109 write!(n, ":").unwrap();110 print_code_location(&mut n, &location, &location).unwrap();111 write!(out, "{:<p$}{}", "", n, p = self.padding,)?;112 }113 let file_names = error114 .trace()115 .0116 .iter()117 .map(|el| &el.location)118 .map(|location| {119 use std::fmt::Write;120 #[allow(clippy::option_if_let_else)]121 if let Some(location) = location {122 let mut resolved_path = self.resolver.resolve(&location.0);123 124 let location = s.map_source_locations(&location.0, &[location.1, location.2]);125 write!(resolved_path, ":").unwrap();126 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();127 write!(resolved_path, ":").unwrap();128 Some(resolved_path)129 } else {130 None131 }132 })133 .collect::<Vec<_>>();134 let align = file_names135 .iter()136 .flatten()137 .map(|e| e.len())138 .max()139 .unwrap_or(0);140 for (el, file) in error.trace().0.iter().zip(file_names) {141 writeln!(out)?;142 if let Some(file) = file {143 write!(144 out,145 "{:<p$}{:<w$} {}",146 "",147 file,148 el.desc,149 p = self.padding,150 w = align151 )?;152 } else {153 write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;154 }155 }156 Ok(())157 }158}159160pub struct JsFormat;161impl TraceFormat for JsFormat {162 fn write_trace(163 &self,164 out: &mut dyn std::fmt::Write,165 s: &State,166 error: &LocError,167 ) -> Result<(), std::fmt::Error> {168 write!(out, "{}", error.error())?;169 for item in error.trace().0.iter() {170 writeln!(out)?;171 let desc = &item.desc;172 if let Some(source) = &item.location {173 let start_end = s.map_source_locations(&source.0, &[source.1, source.2]);174175 write!(176 out,177 " at {} ({}:{}:{})",178 desc,179 source.0.to_str().unwrap(),180 start_end[0].line,181 start_end[0].column,182 )?;183 } else {184 write!(out, " during {}", desc)?;185 }186 }187 Ok(())188 }189}190191192#[cfg(feature = "explaining-traces")]193pub struct ExplainingFormat {194 pub resolver: PathResolver,195}196#[cfg(feature = "explaining-traces")]197impl TraceFormat for ExplainingFormat {198 fn write_trace(199 &self,200 out: &mut dyn std::fmt::Write,201 s: &State,202 error: &LocError,203 ) -> Result<(), std::fmt::Error> {204 write!(out, "{}", error.error())?;205 if let Error::ImportSyntaxError {206 path,207 source_code,208 error,209 } = error.error()210 {211 writeln!(out)?;212 let offset = error.location.offset;213 let location = offset_to_location(source_code, &[offset])214 .into_iter()215 .next()216 .unwrap();217 let mut end_location = location.clone();218 end_location.offset += 1;219220 self.print_snippet(221 out,222 source_code,223 path,224 &location,225 &end_location,226 "syntax error",227 )?;228 }229 let trace = &error.trace();230 for item in trace.0.iter() {231 writeln!(out)?;232 let desc = &item.desc;233 if let Some(source) = &item.location {234 let start_end = s.map_source_locations(&source.0, &[source.1, source.2]);235 self.print_snippet(236 out,237 &s.get_source(&source.0).unwrap(),238 &source.0,239 &start_end[0],240 &start_end[1],241 desc,242 )?;243 } else {244 write!(out, "{}", desc)?;245 }246 }247 Ok(())248 }249}250251impl ExplainingFormat {252 fn print_snippet(253 &self,254 out: &mut dyn std::fmt::Write,255 source: &str,256 origin: &Path,257 start: &CodeLocation,258 end: &CodeLocation,259 desc: &str,260 ) -> Result<(), std::fmt::Error> {261 use annotate_snippets::{262 display_list::{DisplayList, FormatOptions},263 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},264 };265266 let source_fragment: String = source267 .chars()268 .skip(start.line_start_offset)269 .take(end.line_end_offset - end.line_start_offset)270 .collect();271272 let origin = self.resolver.resolve(origin);273 let snippet = Snippet {274 opt: FormatOptions {275 color: true,276 ..Default::default()277 },278 title: None,279 footer: vec![],280 slices: vec![Slice {281 source: &source_fragment,282 line_start: start.line,283 origin: Some(&origin),284 fold: false,285 annotations: vec![SourceAnnotation {286 label: desc,287 annotation_type: AnnotationType::Error,288 range: (289 start.offset - start.line_start_offset,290 (end.offset - start.line_start_offset).min(source_fragment.len()),291 ),292 }],293 }],294 };295296 let dl = DisplayList::from(snippet);297 write!(out, "{}", dl)?;298299 Ok(())300 }301}