git.delta.rocks / jrsonnet / refs/commits / db14944a7cbe

difftreelog

source

crates/jrsonnet-evaluator/src/trace/mod.rs6.5 KiBsourcehistory
1mod location;23use crate::{error::Error, EvaluationState, LocError};4pub use location::*;5use std::path::PathBuf;67/// The way paths should be displayed8pub enum PathResolver {9	/// Only filename10	FileName,11	/// Absolute path12	Absolute,13	/// Path relative to base directory14	Relative(PathBuf),15}1617impl PathResolver {18	pub fn resolve(&self, from: &PathBuf) -> String {19		match self {20			Self::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),21			Self::Absolute => from.to_string_lossy().into_owned(),22			Self::Relative(base) => {23				if from.is_relative() {24					return from.to_string_lossy().into_owned();25				}26				pathdiff::diff_paths(from, base)27					.unwrap()28					.to_string_lossy()29					.into_owned()30			}31		}32	}33}3435/// Implements pretty-printing of traces36pub trait TraceFormat {37	fn write_trace(38		&self,39		out: &mut dyn std::fmt::Write,40		evaluation_state: &EvaluationState,41		error: &LocError,42	) -> Result<(), std::fmt::Error>;43	// fn print_trace(44	// 	&self,45	// 	evaluation_state: &EvaluationState,46	// 	error: &LocError,47	// ) -> Result<(), std::fmt::Error> {48	// 	self.write_trace(&mut std::fmt::stdout(), evaluation_state, error)49	// }50}5152fn print_code_location(53	out: &mut impl std::fmt::Write,54	start: &CodeLocation,55	end: &CodeLocation,56) -> Result<(), std::fmt::Error> {57	if start.line == end.line {58		if start.column == end.column {59			write!(out, "{}:{}", start.line, end.column - 1)?;60		} else {61			write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;62		}63	} else {64		write!(65			out,66			"{}:{}-{}:{}",67			start.line,68			end.column - 1,69			start.line,70			end.column71		)?;72	}73	Ok(())74}7576/// vanilla-like jsonnet formatting77pub struct CompactFormat {78	pub resolver: PathResolver,79	pub padding: usize,80}8182impl TraceFormat for CompactFormat {83	fn write_trace(84		&self,85		out: &mut dyn std::fmt::Write,86		evaluation_state: &EvaluationState,87		error: &LocError,88	) -> Result<(), std::fmt::Error> {89		writeln!(out, "{}", error.error())?;90		match error.error() {91			Error::ImportSyntaxError {92				path,93				source_code,94				error,95			} => {96				use std::fmt::Write;97				let mut n = self.resolver.resolve(&path);98				let mut offset = error.location.offset;99				let mut is_eof = false;100				if offset >= source_code.len() {101					offset = source_code.len() - 1;102					is_eof = true;103				}104				let mut location = offset_to_location(&source_code, &[offset])105					.into_iter()106					.next()107					.unwrap();108				if is_eof {109					location.column += 1;110				}111112				write!(n, ":").unwrap();113				print_code_location(&mut n, &location, &location).unwrap();114				write!(out, "{:<p$}{}", "", n, p = self.padding,)?;115			}116			_ => {}117		}118		let file_names = error119			.trace()120			.0121			.iter()122			.map(|el| {123				let resolved_path = self.resolver.resolve(&el.location.0);124				// TODO: Process all trace elements first125				let location = evaluation_state126					.map_source_locations(&el.location.0, &[el.location.1, el.location.2]);127				(resolved_path, location)128			})129			.map(|(mut n, location)| {130				use std::fmt::Write;131				write!(n, ":").unwrap();132				print_code_location(&mut n, &location[0], &location[1]).unwrap();133				n134			})135			.collect::<Vec<_>>();136		let align = file_names.iter().map(|e| e.len()).max().unwrap_or(0);137		for (i, (el, file)) in error.trace().0.iter().zip(file_names).enumerate() {138			if i != 0 {139				writeln!(out)?;140			}141			write!(142				out,143				"{:<p$}{:<w$}: {}",144				"",145				file,146				el.desc,147				p = self.padding,148				w = align149			)?;150		}151		Ok(())152	}153}154155pub struct JSFormat;156impl TraceFormat for JSFormat {157	fn write_trace(158		&self,159		out: &mut dyn std::fmt::Write,160		evaluation_state: &EvaluationState,161		error: &LocError,162	) -> Result<(), std::fmt::Error> {163		writeln!(out, "{}", error.error())?;164		for (i, item) in error.trace().0.iter().enumerate() {165			if i != 0 {166				writeln!(out)?;167			}168			let desc = &item.desc;169			let source = item.location.clone();170			let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);171172			write!(173				out,174				"    at {} ({}:{}:{})",175				desc,176				source.0.to_str().unwrap(),177				start_end[0].line,178				start_end[0].column,179			)?;180		}181		Ok(())182	}183}184185/// rustc-like trace displaying186#[cfg(feature = "explaining-traces")]187pub struct ExplainingFormat {188	pub resolver: PathResolver,189}190#[cfg(feature = "explaining-traces")]191impl TraceFormat for ExplainingFormat {192	fn write_trace(193		&self,194		out: &mut dyn std::fmt::Write,195		evaluation_state: &EvaluationState,196		error: &LocError,197	) -> Result<(), std::fmt::Error> {198		writeln!(out, "{}", error.error())?;199		match error.error() {200			Error::ImportSyntaxError {201				path,202				source_code,203				error,204			} => {205				let mut offset = error.location.offset;206				if offset >= source_code.len() {207					offset = source_code.len() - 1;208				}209				let mut location = offset_to_location(&source_code, &[offset])210					.into_iter()211					.next()212					.unwrap();213				if location.column >= 1 {214					location.column -= 1;215				}216217				self.print_snippet(218					out,219					&source_code,220					&path,221					&location,222					&location,223					"^ syntax error",224				)?;225			}226			_ => {}227		}228		let trace = &error.trace();229		for item in trace.0.iter() {230			let desc = &item.desc;231			let source = item.location.clone();232			let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);233234			self.print_snippet(235				out,236				&evaluation_state.get_source(&source.0).unwrap(),237				&source.0,238				&start_end[0],239				&start_end[1],240				desc,241			)?;242		}243		Ok(())244	}245}246247impl ExplainingFormat {248	fn print_snippet(249		&self,250		out: &mut dyn std::fmt::Write,251		source: &str,252		origin: &PathBuf,253		start: &CodeLocation,254		end: &CodeLocation,255		desc: &str,256	) -> Result<(), std::fmt::Error> {257		use annotate_snippets::{258			display_list::{DisplayList, FormatOptions},259			snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},260		};261262		let source_fragment: String = source263			.chars()264			.skip(start.line_start_offset)265			.take(end.line_end_offset - end.line_start_offset)266			.collect();267268		let origin = self.resolver.resolve(&origin);269		let snippet = Snippet {270			opt: FormatOptions {271				color: true,272				..Default::default()273			},274			title: None,275			footer: vec![],276			slices: vec![Slice {277				source: &source_fragment,278				line_start: start.line,279				origin: Some(&origin),280				fold: false,281				annotations: vec![SourceAnnotation {282					label: desc,283					annotation_type: AnnotationType::Error,284					range: (285						start.offset - start.line_start_offset,286						end.offset - start.line_start_offset,287					),288				}],289			}],290		};291292		let dl = DisplayList::from(snippet);293		writeln!(out, "{}", dl)?;294295		Ok(())296	}297}