difftreelog
feat(ir) source url
in: master
7 files changed
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- 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
})?,
);
}
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth1#[cfg(feature = "explaining-traces")]2use std::cell::RefCell;3use std::{4 any::Any,5 path::{Component, Path, PathBuf},6};78use jrsonnet_gcmodule::Trace;9use jrsonnet_ir::CodeLocation;10#[cfg(feature = "explaining-traces")]11use jrsonnet_ir::Span;1213use crate::{Error, error::ErrorKind};1415/// The way paths should be displayed16#[derive(Clone, Trace)]17pub enum PathResolver {18 /// Only filename19 FileName,20 /// Absolute path21 Absolute,22 /// Path relative to base directory23 Relative(PathBuf),24}2526impl PathResolver {27 /// Will return `Self::Relative(cwd)`, or `Self::Absolute` on cwd failure28 pub fn new_cwd_fallback() -> Self {29 std::env::current_dir().map_or(Self::Absolute, Self::Relative)30 }31 pub fn resolve(&self, from: &Path) -> String {32 match self {33 Self::FileName => from34 .file_name()35 .expect("file name exists")36 .to_string_lossy()37 .into_owned(),38 Self::Absolute => from.to_string_lossy().into_owned(),39 Self::Relative(base) => {40 if from.is_relative() {41 return from.to_string_lossy().into_owned();42 }43 let diff = pathdiff::diff_paths(from, base).expect("base is absolute");44 let parents = diff45 .components()46 .take_while(|c| matches!(c, Component::ParentDir))47 .count();48 let base_depth = base49 .components()50 .filter(|c| matches!(c, Component::Normal(_)))51 .count();52 if parents > 0 && parents >= base_depth {53 return from.to_string_lossy().into_owned();54 }55 diff.to_string_lossy().into_owned()56 }57 }58 }59}6061/// Implements pretty-printing of traces62#[allow(clippy::module_name_repetitions)]63pub trait TraceFormat: Trace {64 fn write_trace(65 &self,66 out: &mut dyn std::fmt::Write,67 error: &Error,68 ) -> Result<(), std::fmt::Error>;69 fn format(&self, error: &Error) -> Result<String, std::fmt::Error> {70 let mut out = String::new();71 self.write_trace(&mut out, error)?;72 Ok(out)73 }74 fn as_any(&self) -> &dyn Any;75 fn as_any_mut(&mut self) -> &mut dyn Any;76}7778fn print_code_location(79 out: &mut impl std::fmt::Write,80 start: &CodeLocation,81 end: &CodeLocation,82) -> Result<(), std::fmt::Error> {83 if start.line == end.line {84 if start.column == end.column {85 write!(out, "{}:{}", start.line, end.column.saturating_sub(1))?;86 } else {87 write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;88 }89 } else {90 write!(91 out,92 "{}:{}-{}:{}",93 start.line,94 end.column.saturating_sub(1),95 start.line,96 end.column97 )?;98 }99 Ok(())100}101102/// vanilla-like jsonnet formatting103#[derive(Trace)]104pub struct CompactFormat {105 pub resolver: PathResolver,106 pub max_trace: usize,107 pub padding: usize,108}109impl Default for CompactFormat {110 fn default() -> Self {111 Self {112 resolver: PathResolver::Absolute,113 max_trace: 20,114 padding: 4,115 }116 }117}118119impl TraceFormat for CompactFormat {120 fn write_trace(121 &self,122 out: &mut dyn std::fmt::Write,123 error: &Error,124 ) -> Result<(), std::fmt::Error> {125 write!(out, "{}", error.error())?;126 if let ErrorKind::ImportSyntaxError { path, error } = error.error() {127 use std::fmt::Write;128129 writeln!(out)?;130 let mut n = path.source_path().path().map_or_else(131 || path.source_path().to_string(),132 |r| self.resolver.resolve(r),133 );134 let mut offset = error.location.1 as usize;135 let is_eof = if offset >= path.code().len() {136 offset = path.code().len().saturating_sub(1);137 true138 } else {139 false140 };141 #[expect(clippy::cast_possible_truncation, reason = "code is limited by 4gb")]142 let mut location = path143 .map_source_locations(&[offset as u32])144 .into_iter()145 .next()146 .unwrap();147 if is_eof {148 location.column += 1;149 }150151 write!(n, ":").unwrap();152 print_code_location(&mut n, &location, &location).unwrap();153 write!(out, "{:<p$}{n}", "", p = self.padding)?;154 }155 let file_names = error156 .trace()157 .0158 .iter()159 .map(|el| &el.location)160 .map(|location| {161 use std::fmt::Write;162 #[allow(clippy::option_if_let_else)]163 if let Some(location) = location {164 let mut resolved_path = match location.0.source_path().path() {165 Some(r) => self.resolver.resolve(r),166 None => location.0.source_path().to_string(),167 };168 // TODO: Process all trace elements first169 let location = location.0.map_source_locations(&[location.1, location.2]);170 write!(resolved_path, ":").unwrap();171 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();172 write!(resolved_path, ":").unwrap();173 Some(resolved_path)174 } else {175 None176 }177 })178 .collect::<Vec<_>>();179 let align = file_names180 .iter()181 .flatten()182 .map(String::len)183 .max()184 .unwrap_or(0);185 for (el, file) in error.trace().0.iter().zip(file_names) {186 writeln!(out)?;187 if let Some(file) = file {188 write!(189 out,190 "{:<p$}{:<w$} {}",191 "",192 file,193 el.desc,194 p = self.padding,195 w = align196 )?;197 } else {198 write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;199 }200 }201 Ok(())202 }203204 fn as_any(&self) -> &dyn Any {205 self206 }207208 fn as_any_mut(&mut self) -> &mut dyn Any {209 self210 }211}212213#[derive(Trace)]214pub struct JsFormat {215 pub max_trace: usize,216}217impl TraceFormat for JsFormat {218 fn write_trace(219 &self,220 out: &mut dyn std::fmt::Write,221 error: &Error,222 ) -> Result<(), std::fmt::Error> {223 write!(out, "{}", error.error())?;224 for item in &error.trace().0 {225 writeln!(out)?;226 let desc = &item.desc;227 if let Some(source) = &item.location {228 let start_end = source.0.map_source_locations(&[source.1, source.2]);229 let resolved_path = source.0.source_path().path().map_or_else(230 || source.0.source_path().to_string(),231 |r| r.display().to_string(),232 );233234 write!(235 out,236 " at {} ({}:{}:{})",237 desc, resolved_path, start_end[0].line, start_end[0].column,238 )?;239 } else {240 write!(out, " during {desc}")?;241 }242 }243 Ok(())244 }245246 fn as_any(&self) -> &dyn Any {247 self248 }249250 fn as_any_mut(&mut self) -> &mut dyn Any {251 self252 }253}254255#[cfg(feature = "explaining-traces")]256#[derive(Trace)]257pub struct HiDocFormat {258 pub resolver: PathResolver,259 pub max_trace: usize,260}261#[cfg(feature = "explaining-traces")]262impl TraceFormat for HiDocFormat {263 fn write_trace(264 &self,265 out: &mut dyn std::fmt::Write,266 error: &Error,267 ) -> Result<(), std::fmt::Error> {268 struct ResetData {269 loc: Span,270 }271 use hi_doc::{Formatting, SnippetBuilder, Text, source_to_ansi};272273 write!(out, "{}", error.error())?;274 if let ErrorKind::ImportSyntaxError { path, error } = error.error() {275 writeln!(out)?;276 let mut builder = SnippetBuilder::new(path.code());277 builder278 .error(Text::fragment("syntax error", Formatting::default()))279 .range(error.location.range())280 .build();281 let source = builder.build();282 let ansi = source_to_ansi(&source);283 write!(out, "{ansi}")?;284 }285 if let ErrorKind::StaticAnalysisError(diagnostics) = error.error() {286 use crate::analyze::DiagLevel;287 let mut builder: Option<SnippetBuilder> = None;288 let mut current_src: Option<&str> = None;289 let flush = |builder: Option<SnippetBuilder>,290 out: &mut dyn std::fmt::Write|291 -> Result<(), std::fmt::Error> {292 if let Some(b) = builder {293 let ansi = source_to_ansi(&b.build());294 write!(out, "\n{}", ansi.trim_end())?;295 }296 Ok(())297 };298 for diag in diagnostics {299 if let Some(span) = &diag.span {300 let src = span.0.code();301 if current_src != Some(src) {302 flush(builder.take(), out)?;303 builder = Some(SnippetBuilder::new(src));304 current_src = Some(src);305 }306 let b = builder.as_mut().unwrap();307 let ab = match diag.level {308 DiagLevel::Error => {309 b.error(Text::fragment(diag.message.clone(), Formatting::default()))310 }311 DiagLevel::Warning => {312 b.warning(Text::fragment(diag.message.clone(), Formatting::default()))313 }314 };315 ab.range(span.range()).build();316 } else {317 flush(builder.take(), out)?;318 current_src = None;319 let prefix = match diag.level {320 DiagLevel::Error => "error",321 DiagLevel::Warning => "warning",322 };323 write!(out, "\n{prefix}: {}", diag.message)?;324 }325 }326 flush(builder, out)?;327 }328 let trace = &error.trace();329 let snippet_builder: RefCell<Option<SnippetBuilder>> = RefCell::new(None);330 let mut last_location: Option<Span> = None;331 let mut flush_builder = |data: Option<ResetData>| {332 use std::fmt::Write;333 let mut out = String::new();334 let location_changed = if let Some(ResetData { loc }) = &data {335 if last_location.as_ref().map(|l| l.0.code()) != Some(loc.0.code()) {336 true337 } else if let (Some(last), new) = (&last_location, loc) {338 // Reverse condition if traceback339 last.1 > new.1 || last.2 > new.2340 } else {341 false342 }343 } else {344 true345 };346 if location_changed {347 if let Some(builder) = snippet_builder.borrow_mut().take() {348 let rendered = builder.build();349 let ansi = source_to_ansi(&rendered);350 if let Some(loc) = &last_location {351 let _ = writeln!(out, "...at {}", loc.0.source_path());352 }353 let _ = write!(out, "{}", ansi.trim_end());354 }355 last_location = None;356357 if let Some(ResetData { loc }) = data {358 *snippet_builder.borrow_mut() = Some(SnippetBuilder::new(loc.0.code()));359 last_location = Some(loc);360 }361 }362 if out.is_empty() {363 return None;364 }365 Some(out)366 };367 for item in &trace.0 {368 let desc = &item.desc;369 if let Some(source) = &item.location {370 if let Some(flushed) = flush_builder(Some(ResetData {371 loc: source.clone(),372 })) {373 writeln!(out)?;374 write!(out, "{flushed}")?;375 }376 let mut builder = snippet_builder.borrow_mut();377 let builder = builder.as_mut().unwrap();378 builder379 .note(Text::fragment(desc, Formatting::default()))380 .range(source.1 as usize..=(source.2 as usize - 1).max(source.1 as usize))381 .build();382 } else {383 if let Some(flushed) = flush_builder(None) {384 writeln!(out)?;385 write!(out, "{flushed}")?;386 }387 writeln!(out)?;388 write!(out, " {desc}")?;389 }390 }391392 if let Some(flushed) = flush_builder(None) {393 writeln!(out)?;394 write!(out, "{flushed}")?;395 }396 Ok(())397 }398399 fn as_any(&self) -> &dyn Any {400 self401 }402403 fn as_any_mut(&mut self) -> &mut dyn Any {404 self405 }406}1#[cfg(feature = "explaining-traces")]2use std::cell::RefCell;3use std::{4 any::Any,5 path::{Component, Path, PathBuf},6};78use jrsonnet_gcmodule::Trace;9use jrsonnet_ir::CodeLocation;10#[cfg(feature = "explaining-traces")]11use jrsonnet_ir::Span;1213use crate::{Error, error::ErrorKind};1415/// The way paths should be displayed16#[derive(Clone, Trace)]17pub enum PathResolver {18 /// Only filename19 FileName,20 /// Absolute path21 Absolute,22 /// Path relative to base directory23 Relative(PathBuf),24}2526impl PathResolver {27 /// Will return `Self::Relative(cwd)`, or `Self::Absolute` on cwd failure28 pub fn new_cwd_fallback() -> Self {29 std::env::current_dir().map_or(Self::Absolute, Self::Relative)30 }31 pub fn resolve(&self, from: &Path) -> String {32 match self {33 Self::FileName => from34 .file_name()35 .expect("file name exists")36 .to_string_lossy()37 .into_owned(),38 Self::Absolute => from.to_string_lossy().into_owned(),39 Self::Relative(base) => {40 if from.is_relative() {41 return from.to_string_lossy().into_owned();42 }43 let diff = pathdiff::diff_paths(from, base).expect("base is absolute");44 let parents = diff45 .components()46 .take_while(|c| matches!(c, Component::ParentDir))47 .count();48 let base_depth = base49 .components()50 .filter(|c| matches!(c, Component::Normal(_)))51 .count();52 if parents > 0 && parents >= base_depth {53 return from.to_string_lossy().into_owned();54 }55 diff.to_string_lossy().into_owned()56 }57 }58 }59}6061/// Implements pretty-printing of traces62#[allow(clippy::module_name_repetitions)]63pub trait TraceFormat: Trace {64 fn write_trace(65 &self,66 out: &mut dyn std::fmt::Write,67 error: &Error,68 ) -> Result<(), std::fmt::Error>;69 fn format(&self, error: &Error) -> Result<String, std::fmt::Error> {70 let mut out = String::new();71 self.write_trace(&mut out, error)?;72 Ok(out)73 }74 fn as_any(&self) -> &dyn Any;75 fn as_any_mut(&mut self) -> &mut dyn Any;76}7778fn print_code_location(79 out: &mut impl std::fmt::Write,80 start: &CodeLocation,81 end: &CodeLocation,82) -> Result<(), std::fmt::Error> {83 if start.line == end.line {84 if start.column == end.column {85 write!(out, "{}:{}", start.line, start.column)?;86 } else {87 write!(88 out,89 "{}:{}-{}",90 start.line,91 start.column,92 end.column.saturating_sub(1)93 )?;94 }95 } else {96 write!(97 out,98 "{}:{}-{}:{}",99 start.line,100 start.column,101 end.line,102 end.column.saturating_sub(1)103 )?;104 }105 Ok(())106}107108/// vanilla-like jsonnet formatting109#[derive(Trace)]110pub struct CompactFormat {111 pub resolver: PathResolver,112 pub max_trace: usize,113 pub padding: usize,114}115impl Default for CompactFormat {116 fn default() -> Self {117 Self {118 resolver: PathResolver::Absolute,119 max_trace: 20,120 padding: 4,121 }122 }123}124125impl TraceFormat for CompactFormat {126 fn write_trace(127 &self,128 out: &mut dyn std::fmt::Write,129 error: &Error,130 ) -> Result<(), std::fmt::Error> {131 write!(out, "{}", error.error())?;132 if let ErrorKind::ImportSyntaxError { path, error } = error.error() {133 use std::fmt::Write;134135 writeln!(out)?;136 let mut n = path.source_path().path().map_or_else(137 || path.source_path().to_string(),138 |r| self.resolver.resolve(r),139 );140 let offset = (error.location.1 as usize).min(path.code().len());141 #[expect(clippy::cast_possible_truncation, reason = "code is limited by 4gb")]142 let location = path143 .map_source_locations(&[offset as u32])144 .into_iter()145 .next()146 .unwrap();147148 write!(n, ":").unwrap();149 print_code_location(&mut n, &location, &location).unwrap();150 write!(out, "{:<p$}{n}", "", p = self.padding)?;151 }152 let file_names = error153 .trace()154 .0155 .iter()156 .map(|el| &el.location)157 .map(|location| {158 use std::fmt::Write;159 #[allow(clippy::option_if_let_else)]160 if let Some(location) = location {161 let mut resolved_path = match location.0.source_path().path() {162 Some(r) => self.resolver.resolve(r),163 None => location.0.source_path().to_string(),164 };165 // TODO: Process all trace elements first166 let location = location.0.map_source_locations(&[location.1, location.2]);167 write!(resolved_path, ":").unwrap();168 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();169 write!(resolved_path, ":").unwrap();170 Some(resolved_path)171 } else {172 None173 }174 })175 .collect::<Vec<_>>();176 let align = file_names177 .iter()178 .flatten()179 .map(String::len)180 .max()181 .unwrap_or(0);182 for (el, file) in error.trace().0.iter().zip(file_names) {183 writeln!(out)?;184 if let Some(file) = file {185 write!(186 out,187 "{:<p$}{:<w$} {}",188 "",189 file,190 el.desc,191 p = self.padding,192 w = align193 )?;194 } else {195 write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;196 }197 }198 Ok(())199 }200201 fn as_any(&self) -> &dyn Any {202 self203 }204205 fn as_any_mut(&mut self) -> &mut dyn Any {206 self207 }208}209210#[derive(Trace)]211pub struct JsFormat {212 pub max_trace: usize,213}214impl TraceFormat for JsFormat {215 fn write_trace(216 &self,217 out: &mut dyn std::fmt::Write,218 error: &Error,219 ) -> Result<(), std::fmt::Error> {220 write!(out, "{}", error.error())?;221 for item in &error.trace().0 {222 writeln!(out)?;223 let desc = &item.desc;224 if let Some(source) = &item.location {225 let start_end = source.0.map_source_locations(&[source.1, source.2]);226 let resolved_path = source.0.source_path().path().map_or_else(227 || source.0.source_path().to_string(),228 |r| r.display().to_string(),229 );230231 write!(232 out,233 " at {} ({}:{}:{})",234 desc, resolved_path, start_end[0].line, start_end[0].column,235 )?;236 } else {237 write!(out, " during {desc}")?;238 }239 }240 Ok(())241 }242243 fn as_any(&self) -> &dyn Any {244 self245 }246247 fn as_any_mut(&mut self) -> &mut dyn Any {248 self249 }250}251252#[cfg(feature = "explaining-traces")]253#[derive(Trace)]254pub struct HiDocFormat {255 pub resolver: PathResolver,256 pub max_trace: usize,257}258#[cfg(feature = "explaining-traces")]259impl TraceFormat for HiDocFormat {260 fn write_trace(261 &self,262 out: &mut dyn std::fmt::Write,263 error: &Error,264 ) -> Result<(), std::fmt::Error> {265 struct ResetData {266 loc: Span,267 }268 use hi_doc::{Formatting, SnippetBuilder, Text, source_to_ansi};269270 write!(out, "{}", error.error())?;271 if let ErrorKind::ImportSyntaxError { path, error } = error.error() {272 writeln!(out)?;273 let mut builder = SnippetBuilder::new(path.code());274 builder275 .error(Text::fragment("syntax error", Formatting::default()))276 .range(error.location.range())277 .build();278 let source = builder.build();279 let ansi = source_to_ansi(&source);280 write!(out, "{ansi}")?;281 }282 if let ErrorKind::StaticAnalysisError(diagnostics) = error.error() {283 use crate::analyze::DiagLevel;284 let mut builder: Option<SnippetBuilder> = None;285 let mut current_src: Option<&str> = None;286 let flush = |builder: Option<SnippetBuilder>,287 out: &mut dyn std::fmt::Write|288 -> Result<(), std::fmt::Error> {289 if let Some(b) = builder {290 let ansi = source_to_ansi(&b.build());291 write!(out, "\n{}", ansi.trim_end())?;292 }293 Ok(())294 };295 for diag in diagnostics {296 if let Some(span) = &diag.span {297 let src = span.0.code();298 if current_src != Some(src) {299 flush(builder.take(), out)?;300 builder = Some(SnippetBuilder::new(src));301 current_src = Some(src);302 }303 let b = builder.as_mut().unwrap();304 let ab = match diag.level {305 DiagLevel::Error => {306 b.error(Text::fragment(diag.message.clone(), Formatting::default()))307 }308 DiagLevel::Warning => {309 b.warning(Text::fragment(diag.message.clone(), Formatting::default()))310 }311 };312 ab.range(span.range()).build();313 } else {314 flush(builder.take(), out)?;315 current_src = None;316 let prefix = match diag.level {317 DiagLevel::Error => "error",318 DiagLevel::Warning => "warning",319 };320 write!(out, "\n{prefix}: {}", diag.message)?;321 }322 }323 flush(builder, out)?;324 }325 let trace = &error.trace();326 let snippet_builder: RefCell<Option<SnippetBuilder>> = RefCell::new(None);327 let mut last_location: Option<Span> = None;328 let mut flush_builder = |data: Option<ResetData>| {329 use std::fmt::Write;330 let mut out = String::new();331 let location_changed = if let Some(ResetData { loc }) = &data {332 if last_location.as_ref().map(|l| l.0.code()) != Some(loc.0.code()) {333 true334 } else if let (Some(last), new) = (&last_location, loc) {335 // Reverse condition if traceback336 last.1 > new.1 || last.2 > new.2337 } else {338 false339 }340 } else {341 true342 };343 if location_changed {344 if let Some(builder) = snippet_builder.borrow_mut().take() {345 let rendered = builder.build();346 let ansi = source_to_ansi(&rendered);347 if let Some(loc) = &last_location {348 let _ = writeln!(out, "...at {}", loc.0.source_path());349 }350 let _ = write!(out, "{}", ansi.trim_end());351 }352 last_location = None;353354 if let Some(ResetData { loc }) = data {355 *snippet_builder.borrow_mut() = Some(SnippetBuilder::new(loc.0.code()));356 last_location = Some(loc);357 }358 }359 if out.is_empty() {360 return None;361 }362 Some(out)363 };364 for item in &trace.0 {365 let desc = &item.desc;366 if let Some(source) = &item.location {367 if let Some(flushed) = flush_builder(Some(ResetData {368 loc: source.clone(),369 })) {370 writeln!(out)?;371 write!(out, "{flushed}")?;372 }373 let mut builder = snippet_builder.borrow_mut();374 let builder = builder.as_mut().unwrap();375 builder376 .note(Text::fragment(desc, Formatting::default()))377 .range(source.1 as usize..=(source.2 as usize - 1).max(source.1 as usize))378 .build();379 } else {380 if let Some(flushed) = flush_builder(None) {381 writeln!(out)?;382 write!(out, "{flushed}")?;383 }384 writeln!(out)?;385 write!(out, " {desc}")?;386 }387 }388389 if let Some(flushed) = flush_builder(None) {390 writeln!(out)?;391 write!(out, "{flushed}")?;392 }393 Ok(())394 }395396 fn as_any(&self) -> &dyn Any {397 self398 }399400 fn as_any_mut(&mut self) -> &mut dyn Any {401 self402 }403}crates/jrsonnet-formatter/src/lib.rsdiffbeforeafterboth--- 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,
crates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth--- 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<IStr> {
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();
crates/jrsonnet-ir/src/lib.rsdiffbeforeafterboth--- 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
crates/jrsonnet-ir/src/location.rsdiffbeforeafterboth--- 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
}
crates/jrsonnet-ir/src/source.rsdiffbeforeafterboth--- 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`]