--- a/Cargo.lock +++ b/Cargo.lock @@ -109,18 +109,6 @@ checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] -name = "ass-stroke" -version = "0.1.0" -source = "git+https://github.com/CertainLach/ass-stroke#e649d7ffb2beb4800143b7a5acfdae0ad3fb6d94" -dependencies = [ - "num-traits", - "rand 0.8.5", - "random_color", - "range-map", - "smallvec", -] - -[[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -422,24 +410,13 @@ [[package]] name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -471,6 +448,19 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] +name = "hi-doc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "748c617f3021ee027bf6f94d54f9c28877467cb79c5847d0dd70c3a6db4da0fc" +dependencies = [ + "num-traits", + "rand", + "random_color", + "range-map", + "smallvec", +] + +[[package]] name = "idna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -545,9 +535,9 @@ name = "jrsonnet" version = "0.5.0-pre95" dependencies = [ - "ass-stroke", "clap", "clap_complete", + "hi-doc", "jrsonnet-cli", "jrsonnet-evaluator", "jrsonnet-gcmodule", @@ -578,6 +568,7 @@ "bincode", "derivative", "hashbrown 0.14.3", + "hi-doc", "jrsonnet-gcmodule", "jrsonnet-interner", "jrsonnet-macros", @@ -596,9 +587,9 @@ name = "jrsonnet-fmt" version = "0.5.0-pre95" dependencies = [ - "ass-stroke", "clap", "dprint-core", + "hi-doc", "indoc", "insta", "jrsonnet-rowan-parser", @@ -1059,20 +1050,6 @@ checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", ] [[package]] @@ -1082,18 +1059,8 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -1103,16 +1070,7 @@ checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -1121,34 +1079,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.12", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] name = "random_color" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f34bd6526786b2ce5141fd37a4084b5da1ebae74595b5b0d05482a7cef7181" +checksum = "0085421bc527effa7ed6d46bac0a28734663c47abe03d80a5e78e441fad85196" dependencies = [ - "rand 0.7.3", + "rand", ] [[package]] @@ -1581,12 +1521,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" --- 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" --- 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 --- 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 --- 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 } --- 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 --- 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> = RefCell::new(None); + let mut last_location: Option = None; + let mut flush_builder = |data: Option| { + 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 + } +}