git.delta.rocks / jrsonnet / refs/commits / 759175b8af18

difftreelog

feat add tracing format based on hi-doc

Yaroslav Bolyukin2024-02-20parent: #9ae683a.patch.diff
in: master

7 files changed

modifiedCargo.lockdiffbeforeafterboth
before · Cargo.lock
199 packageslockfile v3
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,7 +16,7 @@
 jrsonnet-types = { path = "./crates/jrsonnet-types", version = "0.5.0-pre95" }
 
 jrsonnet-gcmodule = "0.3.6"
-ass-stroke = { git = "https://github.com/CertainLach/ass-stroke", version = "0.1.0" }
+hi-doc = "0.1.0"
 
 serde = "1.0.197"
 serde_json = "1.0.114"
modifiedcmds/jrsonnet-fmt/Cargo.tomldiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/Cargo.toml
+++ b/cmds/jrsonnet-fmt/Cargo.toml
@@ -8,7 +8,7 @@
 jrsonnet-rowan-parser.workspace = true
 insta.workspace = true
 indoc.workspace = true
-ass-stroke.workspace = true
+hi-doc.workspace = true
 clap = { workspace = true, features = ["derive"] }
 tempfile.workspace = true
 thiserror.workspace = true
modifiedcmds/jrsonnet/Cargo.tomldiffbeforeafterboth
--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -58,4 +58,4 @@
 clap_complete.workspace = true
 serde_json.workspace = true
 serde = { workspace = true, features = ["derive"] }
-ass-stroke.workspace = true
+hi-doc.workspace = true
modifiedcrates/jrsonnet-cli/src/trace.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/trace.rs
+++ b/crates/jrsonnet-cli/src/trace.rs
@@ -1,5 +1,7 @@
 use clap::{Parser, ValueEnum};
-use jrsonnet_evaluator::trace::{CompactFormat, ExplainingFormat, PathResolver, TraceFormat};
+use jrsonnet_evaluator::trace::{
+	AssStrokeFormat, CompactFormat, ExplainingFormat, PathResolver, TraceFormat,
+};
 
 #[derive(PartialEq, Eq, ValueEnum, Clone)]
 pub enum TraceFormatName {
@@ -7,6 +9,8 @@
 	Compact,
 	/// Display source code with attached trace annotations
 	Explaining,
+	/// Experimental trace formatting based on hi-doc library
+	HiDoc,
 }
 
 #[derive(Parser)]
@@ -38,6 +42,10 @@
 				resolver,
 				max_trace,
 			}),
+			TraceFormatName::HiDoc => Box::new(AssStrokeFormat {
+				resolver,
+				max_trace,
+			}),
 		};
 		format
 	}
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -10,7 +10,7 @@
 [features]
 default = ["explaining-traces"]
 # Rustc-like trace visualization
-explaining-traces = ["annotate-snippets"]
+explaining-traces = ["annotate-snippets", "hi-doc"]
 # Allows library authors to throw custom errors
 anyhow-error = ["anyhow"]
 # Adds ability to build import closure in async
@@ -54,6 +54,8 @@
 bincode = { workspace = true, optional = true }
 # Explaining traces
 annotate-snippets = { workspace = true, optional = true }
+# Better explaining traces
+hi-doc = { workspace = true, optional = true }
 # Bigint
 num-bigint = { workspace = true, features = ["serde"], optional = true }
 derivative.workspace = true
modifiedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -1,10 +1,11 @@
 use std::{
 	any::Any,
+	cell::RefCell,
 	path::{Path, PathBuf},
 };
 
 use jrsonnet_gcmodule::Trace;
-use jrsonnet_parser::{CodeLocation, Source};
+use jrsonnet_parser::{CodeLocation, ExprLocation, Source};
 
 use crate::{error::ErrorKind, Error};
 
@@ -303,6 +304,7 @@
 	}
 }
 
+#[cfg(feature = "explaining-traces")]
 impl ExplainingFormat {
 	fn print_snippet(
 		&self,
@@ -363,3 +365,113 @@
 		Ok(())
 	}
 }
+
+#[cfg(feature = "explaining-traces")]
+#[derive(Trace)]
+pub struct AssStrokeFormat {
+	pub resolver: PathResolver,
+	pub max_trace: usize,
+}
+#[cfg(feature = "explaining-traces")]
+impl TraceFormat for AssStrokeFormat {
+	fn write_trace(
+		&self,
+		out: &mut dyn std::fmt::Write,
+		error: &Error,
+	) -> Result<(), std::fmt::Error> {
+		struct ResetData {
+			loc: ExprLocation,
+		}
+		use hi_doc::{source_to_ansi, Formatting, SnippetBuilder, Text};
+
+		write!(out, "{}", error.error())?;
+		if let ErrorKind::ImportSyntaxError { path, error } = error.error() {
+			writeln!(out)?;
+			let offset = error.location.offset;
+			let mut builder = SnippetBuilder::new(path.code());
+			builder
+				.error(Text::single("syntax error".chars(), Formatting::default()))
+				.range(offset..=offset)
+				.build();
+			let source = builder.build();
+			let ansi = source_to_ansi(&source);
+			write!(out, "{ansi}")?;
+		}
+		let trace = &error.trace();
+		let snippet_builder: RefCell<Option<SnippetBuilder>> = RefCell::new(None);
+		let mut last_location: Option<ExprLocation> = None;
+		let mut flush_builder = |data: Option<ResetData>| {
+			use std::fmt::Write;
+			let mut out = String::new();
+			let location_changed = if let Some(ResetData { loc }) = &data {
+				if last_location.as_ref().map(|l| l.0.code()) != Some(loc.0.code()) {
+					true
+				} else if let (Some(last), new) = (&last_location, loc) {
+					// Reverse condition if traceback
+					last.1 > new.1 || last.2 > new.2
+				} else {
+					false
+				}
+			} else {
+				true
+			};
+			if location_changed {
+				if let Some(builder) = snippet_builder.borrow_mut().take() {
+					let rendered = builder.build();
+					let ansi = source_to_ansi(&rendered);
+					if let Some(loc) = &last_location {
+						let _ = writeln!(out, "...because of {}", loc.0.source_path());
+					}
+					let _ = write!(out, "{}", ansi.trim_end());
+				}
+				last_location = None;
+
+				if let Some(ResetData { loc }) = data {
+					*snippet_builder.borrow_mut() = Some(SnippetBuilder::new(loc.0.code()));
+					last_location = Some(loc);
+				}
+			}
+			if out.is_empty() {
+				return None;
+			}
+			Some(out)
+		};
+		for item in &trace.0 {
+			let desc = &item.desc;
+			if let Some(source) = &item.location {
+				if let Some(flushed) = flush_builder(Some(ResetData {
+					loc: source.clone(),
+				})) {
+					writeln!(out)?;
+					write!(out, "{flushed}")?;
+				}
+				let mut builder = snippet_builder.borrow_mut();
+				let builder = builder.as_mut().unwrap();
+				builder
+					.note(Text::single(desc.chars(), Formatting::default()))
+					.range(source.1 as usize..=(source.2 as usize - 1).max(source.1 as usize))
+					.build();
+			} else {
+				if let Some(flushed) = flush_builder(None) {
+					writeln!(out)?;
+					write!(out, "{flushed}")?;
+				}
+				write!(out, "{desc}")?;
+			}
+		}
+
+		if let Some(flushed) = flush_builder(None) {
+			writeln!(out)?;
+			write!(out, "{flushed}")?;
+		}
+		Ok(())
+	}
+
+	fn as_any(&self) -> &dyn Any {
+		self
+	}
+
+	fn as_any_mut(&mut self) -> &mut dyn Any {
+		self
+	}
+}