From 3738ed2a535413183fd1ecc6451819fb5dfa7bdf Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Tue, 05 May 2026 00:54:30 +0000 Subject: [PATCH] feat(ir): source url --- --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -35,14 +35,14 @@ pub use ctx::*; pub use dynamic::*; -pub use error::{Error, ErrorKind::*, Result, ResultExt}; +pub use error::{Error, ErrorKind::*, Result, ResultExt, StackTraceElement}; pub use evaluate::ensure_sufficient_stack; use function::CallLocation; pub use import::*; use jrsonnet_gcmodule::{Cc, Trace, cc_dyn}; pub use jrsonnet_interner::{IBytes, IStr}; use jrsonnet_ir::Expr; -pub use jrsonnet_ir::{NumValue, Source, SourcePath, Span}; +pub use jrsonnet_ir::{NumValue, Source, SourcePath, SourceUrl, SourceVirtual, Span}; #[doc(hidden)] pub use jrsonnet_macros; @@ -396,9 +396,17 @@ file.parsed = Some( parse_jsonnet(&code, file_name.clone()) .map(Rc::new) - .map_err(|e| ImportSyntaxError { - path: file_name.clone(), - error: Box::new(e), + .map_err(|e| { + let span = e.location.clone(); + let mut err = Error::from(ImportSyntaxError { + path: file_name.clone(), + error: Box::new(e), + }); + err.trace_mut().0.push(StackTraceElement { + location: Some(span), + desc: "parse imported".to_string(), + }); + err })?, ); } --- a/crates/jrsonnet-evaluator/src/trace/mod.rs +++ b/crates/jrsonnet-evaluator/src/trace/mod.rs @@ -82,18 +82,24 @@ ) -> Result<(), std::fmt::Error> { if start.line == end.line { if start.column == end.column { - write!(out, "{}:{}", start.line, end.column.saturating_sub(1))?; + write!(out, "{}:{}", start.line, start.column)?; } else { - write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?; + write!( + out, + "{}:{}-{}", + start.line, + start.column, + end.column.saturating_sub(1) + )?; } } else { write!( out, "{}:{}-{}:{}", - start.line, - end.column.saturating_sub(1), start.line, - end.column + start.column, + end.line, + end.column.saturating_sub(1) )?; } Ok(()) @@ -131,22 +137,13 @@ || path.source_path().to_string(), |r| self.resolver.resolve(r), ); - let mut offset = error.location.1 as usize; - let is_eof = if offset >= path.code().len() { - offset = path.code().len().saturating_sub(1); - true - } else { - false - }; + let offset = (error.location.1 as usize).min(path.code().len()); #[expect(clippy::cast_possible_truncation, reason = "code is limited by 4gb")] - let mut location = path + let location = path .map_source_locations(&[offset as u32]) .into_iter() .next() .unwrap(); - if is_eof { - location.column += 1; - } write!(n, ":").unwrap(); print_code_location(&mut n, &location, &location).unwrap(); --- a/crates/jrsonnet-formatter/src/lib.rs +++ b/crates/jrsonnet-formatter/src/lib.rs @@ -895,10 +895,16 @@ } } +#[derive(Default)] pub struct FormatOptions { // 0 for hard tabs, otherwise number of spaces pub indent: u8, } +impl FormatOptions { + pub fn new() -> Self { + Self::default() + } +} #[allow( clippy::result_large_err, --- a/crates/jrsonnet-ir-parser/src/lib.rs +++ b/crates/jrsonnet-ir-parser/src/lib.rs @@ -220,10 +220,7 @@ fn ident(p: &mut Parser<'_>) -> Result { if !p.at(SyntaxKind::IDENT) { - return Err(p.error(format!( - "expected identifier, got {}", - p.current_desc() - ))); + return Err(p.error(format!("expected identifier, got {}", p.current_desc()))); } let text = p.text(); p.eat_any(); --- a/crates/jrsonnet-ir/src/lib.rs +++ b/crates/jrsonnet-ir/src/lib.rs @@ -15,7 +15,7 @@ pub use location::CodeLocation; pub use source::{ Source, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath, - SourcePathT, SourceVirtual, + SourcePathT, SourceUrl, SourceVirtual, }; // It seels to be a wrong place for this kind of stuff, but as it would also be used for static analysis and --- a/crates/jrsonnet-ir/src/location.rs +++ b/crates/jrsonnet-ir/src/location.rs @@ -29,7 +29,7 @@ return [CodeLocation::default(); S]; } let mut line = 1; - let mut column = 1; + let mut column = 0; let max_offset = *offsets.iter().max().expect("offsets is not empty"); let mut offset_map = offsets @@ -63,7 +63,7 @@ } if ch == '\n' { line += 1; - column = 1; + column = 0; for idx in with_no_known_line_ending.drain(..) { out[idx].line_end_offset = pos; @@ -98,14 +98,14 @@ CodeLocation { offset: 0, line: 1, - column: 2, + column: 1, line_start_offset: 0, line_end_offset: 11, }, CodeLocation { offset: 14, line: 2, - column: 4, + column: 3, line_start_offset: 12, line_end_offset: 67 } --- a/crates/jrsonnet-ir/src/source.rs +++ b/crates/jrsonnet-ir/src/source.rs @@ -8,6 +8,7 @@ use jrsonnet_gcmodule::Acyclic; use jrsonnet_interner::{IBytes, IStr}; +use url::Url; use crate::location::{CodeLocation, location_to_offset, offset_to_location}; @@ -186,6 +187,28 @@ any_ext_impl!(SourcePathT); } +#[derive(Acyclic, Hash, PartialEq, Eq, Debug)] +pub struct SourceUrl(Url); +impl SourceUrl { + pub fn new(url: Url) -> Self { + Self(url) + } +} +impl Display for SourceUrl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} +impl SourcePathT for SourceUrl { + fn is_default(&self) -> bool { + false + } + fn path(&self) -> Option<&Path> { + None + } + any_ext_impl!(SourcePathT); +} + /// Represents path to the directory on the disk /// /// See also [`SourceFile`] -- gitstuff