difftreelog
test import formatting test-cases from rustanka
in: master
45 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -156,6 +156,16 @@
]
[[package]]
+name = "bstr"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
name = "bumpalo"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -453,6 +463,19 @@
]
[[package]]
+name = "globset"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -548,9 +571,11 @@
checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4"
dependencies = [
"console",
+ "globset",
"once_cell",
"similar",
"tempfile",
+ "walkdir",
]
[[package]]
@@ -797,6 +822,12 @@
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
name = "logos"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1157,6 +1188,15 @@
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
name = "saphyr-parser-bw"
version = "0.0.607"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1491,6 +1531,16 @@
]
[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1545,6 +1595,15 @@
]
[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,7 +38,7 @@
# Parsing, manifestification is implemented manually everywhere
serde = "1.0.228"
serde_json = "1.0.149"
-serde-saphyr = {version = "0.0.17", default-features = false}
+serde-saphyr = { version = "0.0.17", default-features = false }
# Error handling
anyhow = "1.0.101"
@@ -63,7 +63,7 @@
mimallocator = "0.1.3"
indoc = "2.0"
-insta = "1.46"
+insta = { version = "1.46", features = ["glob"] }
tempfile = "3.24"
pathdiff = "0.2.3"
hashbrown = "0.16.1"
crates/jrsonnet-formatter/src/lib.rsdiffbeforeafterboth1use std::{any::type_name, rc::Rc};23use children::{children_between, trivia_before};4use dprint_core::formatting::{5 condition_helpers::is_multiple_lines,6 condition_resolvers::true_resolver,7 ir_helpers::{new_line_group, with_indent},8 ConditionResolver, ConditionResolverContext, LineNumber, PrintItems, PrintOptions,9};10use hi_doc::{Formatting, SnippetBuilder};11use jrsonnet_rowan_parser::{12 nodes::{13 Arg, ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,14 DestructRest, Expr, ExprBase, FieldName, ForSpec, IfSpec, ImportKind, Literal, Member,15 Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Stmt, Suffix, Text,16 UnaryOperator, Visibility,17 },18 AstNode, AstToken as _, SyntaxToken,19};2021use crate::{22 children::{trivia_after, Child, EndingComments},23 comments::{format_comments, CommentLocation},24};2526mod children;27mod comments;28#[cfg(test)]29mod tests;3031fn with_indent_eoi(cond: ConditionResolver, o: PrintItems, e: EndingComments) -> PrintItems {32 let end_comments_items = {33 let mut items = PrintItems::new();34 if e.should_start_with_newline {35 p!(&mut items, nl);36 }37 format_comments(&e.trivia, CommentLocation::EndOfItems, &mut items);38 items.into_rc_path()39 };40 let items =41 new_line_group(pi!(@i; items(o.into()) items(end_comments_items.into()))).into_rc_path();4243 let indented = with_indent(pi!(@i; nl items(items.into())));4445 pi!(@i; if_else("indented body", cond, items(indented))(str(" ") items(items.into())))46}4748pub trait Printable {49 fn print(&self, out: &mut PrintItems);50}5152macro_rules! pi {53 (@i; $($t:tt)*) => {{54 #[allow(unused_mut)]55 let mut o = dprint_core::formatting::PrintItems::new();56 pi!(@s; o: $($t)*);57 o58 }};59 (@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{60 $o.push_string($e.to_owned());61 pi!(@s; $o: $($t)*);62 }};63 (@s; $o:ident: string($e:expr $(,)?) $($t:tt)*) => {{64 $o.push_string($e);65 pi!(@s; $o: $($t)*);66 }};67 (@s; $o:ident: nl $($t:tt)*) => {{68 $o.push_signal(dprint_core::formatting::Signal::NewLine);69 pi!(@s; $o: $($t)*);70 }};71 (@s; $o:ident: sonl $($t:tt)*) => {{72 $o.push_signal(dprint_core::formatting::Signal::SpaceOrNewLine);73 pi!(@s; $o: $($t)*);74 }};75 (@s; $o:ident: tab $($t:tt)*) => {{76 $o.push_signal(dprint_core::formatting::Signal::Tab);77 pi!(@s; $o: $($t)*);78 }};79 (@s; $o:ident: >i $($t:tt)*) => {{80 $o.push_signal(dprint_core::formatting::Signal::StartIndent);81 pi!(@s; $o: $($t)*);82 }};83 (@s; $o:ident: <i $($t:tt)*) => {{84 $o.push_signal(dprint_core::formatting::Signal::FinishIndent);85 pi!(@s; $o: $($t)*);86 }};87 (@s; $o:ident: info($v:expr) $($t:tt)*) => {{88 $o.push_info($v);89 pi!(@s; $o: $($t)*);90 }};91 (@s; $o:ident: ln_anchor($v:expr) $($t:tt)*) => {{92 $o.push_anchor(LineNumberAnchor::new($v));93 pi!(@s; $o: $($t)*);94 }};95 (@s; $o:ident: if($s:literal, $cond:expr, $($i:tt)*) $($t:tt)*) => {{96 $o.push_condition(dprint_core::formatting::conditions::if_true(97 $s,98 $cond.clone(),99 {100 let mut o = PrintItems::new();101 p!(o, $($i)*);102 o103 },104 ));105 pi!(@s; $o: $($t)*);106 }};107 (@s; $o:ident: if_else($s:literal, $cond:expr, $($i:tt)*)($($e:tt)+) $($t:tt)*) => {{108 $o.push_condition(dprint_core::formatting::conditions::if_true_or(109 $s,110 $cond.clone(),111 {112 let mut o = PrintItems::new();113 p!(o, $($i)*);114 o115 },116 {117 let mut o = PrintItems::new();118 p!(o, $($e)*);119 o120 },121 ));122 pi!(@s; $o: $($t)*);123 }};124 (@s; $o:ident: if_not($s:literal, $cond:expr, $($e:tt)*) $($t:tt)*) => {{125 $o.push_condition(dprint_core::formatting::conditions::if_true_or(126 $s,127 $cond.clone(),128 {129 let o = PrintItems::new();130 o131 },132 {133 let mut o = PrintItems::new();134 p!(o, $($e)*);135 o136 },137 ));138 pi!(@s; $o: $($t)*);139 }};140 (@s; $o:ident: {$expr:expr} $($t:tt)*) => {{141 $expr.print($o);142 pi!(@s; $o: $($t)*);143 }};144 (@s; $o:ident: items($expr:expr) $($t:tt)*) => {{145 $o.extend($expr);146 pi!(@s; $o: $($t)*);147 }};148 (@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{149 if $e {150 pi!(@s; $o: $($then)*);151 }152 pi!(@s; $o: $($t)*);153 }};154 (@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{155 if $e {156 pi!(@s; $o: $($then)*);157 } else {158 pi!(@s; $o: $($else)*);159 }160 pi!(@s; $o: $($t)*);161 }};162 (@s; $i:ident:) => {}163}164macro_rules! p {165 ($o:ident, $($t:tt)*) => {166 pi!(@s; $o: $($t)*)167 };168 (&mut $o:ident, $($t:tt)*) => {169 let om = &mut $o;170 pi!(@s; om: $($t)*)171 };172}173pub(crate) use p;174pub(crate) use pi;175176impl<P> Printable for Option<P>177where178 P: Printable,179{180 fn print(&self, out: &mut PrintItems) {181 if let Some(v) = self {182 v.print(out);183 } else {184 p!(185 out,186 string(format!(187 "/*missing {}*/",188 type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")189 ),)190 );191 }192 }193}194195impl Printable for SyntaxToken {196 fn print(&self, out: &mut PrintItems) {197 p!(out, string(self.to_string()));198 }199}200201impl Printable for Text {202 fn print(&self, out: &mut PrintItems) {203 p!(out, string(format!("{}", self)));204 }205}206impl Printable for Number {207 fn print(&self, out: &mut PrintItems) {208 p!(out, string(format!("{}", self)));209 }210}211212impl Printable for Name {213 fn print(&self, out: &mut PrintItems) {214 p!(out, { self.ident_lit() });215 }216}217218impl Printable for DestructRest {219 fn print(&self, out: &mut PrintItems) {220 p!(out, str("..."));221 if let Some(name) = self.into() {222 p!(out, { name });223 }224 }225}226227impl Printable for Destruct {228 fn print(&self, out: &mut PrintItems) {229 match self {230 Self::DestructFull(f) => {231 p!(out, { f.name() });232 }233 Self::DestructSkip(_) => p!(out, str("?")),234 Self::DestructArray(a) => {235 p!(out, str("[") >i nl);236 for el in a.destruct_array_parts() {237 match el {238 DestructArrayPart::DestructArrayElement(e) => {239 p!(out, {e.destruct()} str(",") nl);240 }241 DestructArrayPart::DestructRest(d) => {242 p!(out, {d} str(",") nl);243 }244 }245 }246 p!(out, <i str("]"));247 }248 Self::DestructObject(o) => {249 p!(out, str("{") >i nl);250 for item in o.destruct_object_fields() {251 p!(out, { item.field() });252 if let Some(des) = item.destruct() {253 p!(out, str(": ") {des});254 }255 if let Some(def) = item.expr() {256 p!(out, str(" = ") {def});257 }258 p!(out, str(",") nl);259 }260 if let Some(rest) = o.destruct_rest() {261 p!(out, {rest} nl);262 }263 p!(out, <i str("}"));264 }265 }266 }267}268269impl Printable for FieldName {270 fn print(&self, out: &mut PrintItems) {271 match self {272 Self::FieldNameFixed(f) => {273 if let Some(id) = f.id() {274 p!(out, { id });275 } else if let Some(str) = f.text() {276 p!(out, { str });277 } else {278 p!(out, str("/*missing FieldName*/"));279 }280 }281 Self::FieldNameDynamic(d) => {282 p!(out, str("[") {d.expr()} str("]"));283 }284 }285 }286}287288impl Printable for Visibility {289 fn print(&self, out: &mut PrintItems) {290 p!(out, string(self.to_string()));291 }292}293294impl Printable for ObjLocal {295 fn print(&self, out: &mut PrintItems) {296 p!(out, str("local ") {self.bind()});297 }298}299300impl Printable for Assertion {301 fn print(&self, out: &mut PrintItems) {302 p!(out, str("assert ") {self.condition()});303 if self.colon_token().is_some() || self.message().is_some() {304 p!(out, str(": ") {self.message()});305 }306 }307}308309impl Printable for ParamsDesc {310 fn print(&self, out: &mut PrintItems) {311 p!(out, str("(") >i nl);312 for param in self.params() {313 p!(out, { param.destruct() });314 if param.assign_token().is_some() || param.expr().is_some() {315 p!(out, str(" = ") {param.expr()});316 }317 p!(out, str(",") nl);318 }319 p!(out, <i str(")"));320 }321}322impl Printable for ArgsDesc {323 fn print(&self, out: &mut PrintItems) {324 let start = LineNumber::new("args start line");325 let end = LineNumber::new("args end line");326 let multi_line = Rc::new(move |condition_context: &mut ConditionResolverContext| {327 is_multiple_lines(condition_context, start, end)328 });329330 let (children, end_comments) = children_between::<Arg>(331 self.syntax().clone(),332 self.l_paren_token().map(Into::into).as_ref(),333 self.r_paren_token().map(Into::into).as_ref(),334 None,335 );336337 fn gen_args(children: Vec<Child<Arg>>, multi_line: ConditionResolver) -> PrintItems {338 let mut _out = PrintItems::new();339 let out = &mut _out;340341 let mut args = children.into_iter().peekable();342 while let Some(ele) = args.next() {343 if ele.should_start_with_newline {344 p!(out, nl);345 }346 format_comments(&ele.before_trivia, CommentLocation::AboveItem, out);347 let arg = ele.value;348 if arg.name().is_some() || arg.assign_token().is_some() {349 p!(out, {arg.name()} str(" = "));350 }351 p!(out, { arg.expr() });352 let has_more = args.peek().is_some();353 if has_more {354 p!(out, str(","));355 } else {356 p!(out, if("trailing comma", multi_line, str(",")));357 }358 format_comments(&ele.inline_trivia, CommentLocation::ItemInline, out);359 if has_more {360 p!(out, if_else("arg separator", multi_line, nl)(sonl));361 }362 }363 _out364 }365366 let args_items = new_line_group(gen_args(children, multi_line.clone())).into_rc_path();367 let args_indented = with_indent(pi!(@i; nl items(args_items.into())));368369 p!(out, str("(") info(start));370 p!(out, if_else("args body", multi_line, items(args_indented) nl)(items(args_items.into())));371 if end_comments.should_start_with_newline {372 p!(out, nl);373 }374 format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);375 p!(out, str(")") info(end));376 }377}378impl Printable for SliceDesc {379 fn print(&self, out: &mut PrintItems) {380 p!(out, str("["));381 if self.from().is_some() {382 p!(out, { self.from() });383 }384 p!(out, str(":"));385 if self.end().is_some() {386 p!(out, { self.end().map(|e| e.expr()) });387 }388 // Keep only one : in case if we don't need step389 if self.step().is_some() {390 p!(out, str(":") {self.step().map(|e|e.expr())});391 }392 p!(out, str("]"));393 }394}395396impl Printable for Member {397 fn print(&self, out: &mut PrintItems) {398 match self {399 Self::MemberBindStmt(b) => {400 p!(out, { b.obj_local() });401 }402 Self::MemberAssertStmt(ass) => {403 p!(out, { ass.assertion() });404 }405 Self::MemberFieldNormal(n) => {406 p!(out, {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()});407 }408 Self::MemberFieldMethod(m) => {409 p!(out, {m.field_name()} {m.params_desc()} {m.visibility()} str(" ") {m.expr()});410 }411 }412 }413}414415impl Printable for ObjBody {416 fn print(&self, out: &mut PrintItems) {417 match self {418 Self::ObjBodyComp(l) => {419 let (children, mut end_comments) = children_between::<Member>(420 l.syntax().clone(),421 l.l_brace_token().map(Into::into).as_ref(),422 Some(423 &(l.comp_specs()424 .next()425 .expect("at least one spec is defined")426 .syntax()427 .clone())428 .into(),429 ),430 None,431 );432 let trailing_for_comp = end_comments.extract_trailing();433 p!(out, str("{") >i nl);434 for mem in children {435 if mem.should_start_with_newline {436 p!(out, nl);437 }438 format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);439 p!(out, {mem.value} str(","));440 format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);441 p!(out, nl);442 }443444 if end_comments.should_start_with_newline {445 p!(out, nl);446 }447 format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);448449 let (compspecs, end_comments) = children_between::<CompSpec>(450 l.syntax().clone(),451 l.member_comps()452 .last()453 .map(|m| m.syntax().clone())454 .map(Into::into)455 .or_else(|| l.l_brace_token().map(Into::into))456 .as_ref(),457 l.r_brace_token().map(Into::into).as_ref(),458 Some(trailing_for_comp),459 );460 for mem in compspecs {461 if mem.should_start_with_newline {462 p!(out, nl);463 }464 format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);465 p!(out, { mem.value });466 format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);467 }468 if end_comments.should_start_with_newline {469 p!(out, nl);470 }471 format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);472473 p!(out, nl <i str("}"));474 }475 Self::ObjBodyMemberList(l) => {476 let (children, end_comments) = children_between::<Member>(477 l.syntax().clone(),478 l.l_brace_token().map(Into::into).as_ref(),479 l.r_brace_token().map(Into::into).as_ref(),480 None,481 );482 if children.is_empty() && end_comments.is_empty() {483 p!(out, str("{ }"));484 return;485 }486487 let source_is_multiline = children.iter().any(|c| c.triggers_multiline)488 || end_comments.should_start_with_newline;489490 let start = LineNumber::new("obj start line");491 let end = LineNumber::new("obj end line");492 let multi_line: ConditionResolver = if source_is_multiline {493 true_resolver()494 } else {495 Rc::new(move |ctx: &mut ConditionResolverContext| {496 is_multiple_lines(ctx, start, end)497 })498 };499500 fn gen_members(501 children: Vec<Child<Member>>,502 multi_line: ConditionResolver,503 ) -> PrintItems {504 let mut _out = PrintItems::new();505 let out = &mut _out;506 let mut members = children.into_iter().peekable();507 while let Some(mem) = members.next() {508 if mem.should_start_with_newline {509 p!(out, nl);510 }511 format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);512 p!(out, { mem.value });513 let has_more = members.peek().is_some();514 if has_more {515 p!(out, str(","));516 } else {517 p!(out, if("trailing comma", multi_line, str(",")));518 }519 format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);520 p!(out, if_else("member separator", multi_line, nl)(sonl));521 }522 _out523 }524525 let members_items =526 new_line_group(gen_members(children, multi_line.clone())).into_rc_path();527528 let members = with_indent_eoi(multi_line, members_items.into(), end_comments);529530 p!(out, str("{") info(start));531 p!(out, items(members));532 p!(out, str("}") info(end));533 }534 }535 }536}537impl Printable for UnaryOperator {538 fn print(&self, out: &mut PrintItems) {539 p!(out, string(self.text().to_string()));540 }541}542impl Printable for BinaryOperator {543 fn print(&self, out: &mut PrintItems) {544 p!(out, string(self.text().to_string()));545 }546}547impl Printable for Bind {548 fn print(&self, out: &mut PrintItems) {549 match self {550 Self::BindDestruct(d) => {551 p!(out, {d.into()} str(" = ") {d.value()});552 }553 Self::BindFunction(f) => {554 p!(out, {f.name()} {f.params()} str(" = ") {f.value()});555 }556 }557 }558}559impl Printable for Literal {560 fn print(&self, out: &mut PrintItems) {561 p!(out, string(self.syntax().to_string()));562 }563}564impl Printable for ImportKind {565 fn print(&self, out: &mut PrintItems) {566 p!(out, string(self.syntax().to_string()));567 }568}569impl Printable for ForSpec {570 fn print(&self, out: &mut PrintItems) {571 p!(out, str("for ") {self.bind()} str(" in ") {self.expr()});572 }573}574impl Printable for IfSpec {575 fn print(&self, out: &mut PrintItems) {576 p!(out, str("if ") {self.expr()});577 }578}579impl Printable for CompSpec {580 fn print(&self, out: &mut PrintItems) {581 match self {582 Self::ForSpec(f) => f.print(out),583 Self::IfSpec(i) => i.print(out),584 }585 }586}587impl Printable for Expr {588 fn print(&self, out: &mut PrintItems) {589 let (stmts, _ending) = children_between::<Stmt>(590 self.syntax().clone(),591 None,592 self.expr_base()593 .as_ref()594 .map(ExprBase::syntax)595 .cloned()596 .map(Into::into)597 .as_ref(),598 None,599 );600 for stmt in stmts {601 p!(out, { stmt.value });602 }603 p!(out, { self.expr_base() });604 let (suffixes, _ending) = children_between::<Suffix>(605 self.syntax().clone(),606 self.expr_base()607 .as_ref()608 .map(ExprBase::syntax)609 .cloned()610 .map(Into::into)611 .as_ref(),612 None,613 None,614 );615 for suffix in suffixes {616 p!(out, { suffix.value });617 }618 }619}620impl Printable for Suffix {621 fn print(&self, out: &mut PrintItems) {622 match self {623 Self::SuffixIndex(i) => {624 if i.question_mark_token().is_some() {625 p!(out, str("?"));626 }627 p!(out, str(".") {i.index()});628 }629 Self::SuffixIndexExpr(e) => {630 if e.question_mark_token().is_some() {631 p!(out, str(".?"));632 }633 p!(out, str("[") {e.index()} str("]"));634 }635 Self::SuffixSlice(d) => {636 p!(out, { d.slice_desc() });637 }638 Self::SuffixApply(a) => {639 p!(out, { a.args_desc() });640 }641 }642 }643}644impl Printable for Stmt {645 fn print(&self, out: &mut PrintItems) {646 match self {647 Self::StmtLocal(l) => {648 let (binds, end_comments) = children_between::<Bind>(649 l.syntax().clone(),650 l.local_kw_token().map(Into::into).as_ref(),651 l.semi_token().map(Into::into).as_ref(),652 None,653 );654 if binds.len() == 1 {655 let bind = &binds[0];656 format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);657 p!(out, str("local ") {bind.value});658 // TODO: keep end_comments, child.inline_trivia somehow, force multiple locals formatting in case of presence?659 } else {660 p!(out,str("local") >i nl);661 for bind in binds {662 if bind.should_start_with_newline {663 p!(out, nl);664 }665 format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);666 p!(out, {bind.value} str(","));667 format_comments(&bind.inline_trivia, CommentLocation::ItemInline, out);668 p!(out, nl);669 }670 if end_comments.should_start_with_newline {671 p!(out, nl);672 }673 format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);674 p!(out,<i);675 }676 p!(out,str(";") nl);677 }678 Self::StmtAssert(a) => {679 p!(out, {a.assertion()} str(";") nl);680 }681 }682 }683}684impl Printable for ExprBase {685 fn print(&self, out: &mut PrintItems) {686 match self {687 Self::ExprBinary(b) => {688 p!(out, {b.lhs()} str(" ") {b.binary_operator()} str(" ") {b.rhs()});689 }690 Self::ExprUnary(u) => p!(out, {u.unary_operator()} {u.rhs()}),691 // Self::ExprSlice(s) => {692 // p!(new: {s.expr()} {s.slice_desc()})693 // }694 // Self::ExprIndex(i) => {695 // p!(new: {i.expr()} str(".") {i.index()})696 // }697 // Self::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),698 // Self::ExprApply(a) => {699 // let mut pi = p!(new: {a.expr()} {a.args_desc()});700 // if a.tailstrict_kw_token().is_some() {701 // p!(out,str(" tailstrict"));702 // }703 // pi704 // }705 Self::ExprObjExtend(ex) => {706 p!(out, {ex.lhs_work()} str(" ") {ex.rhs_work()});707 }708 Self::ExprParened(p) => {709 p!(out, str("(") {p.expr()} str(")"));710 }711 Self::ExprString(s) => p!(out, { s.text() }),712 Self::ExprNumber(n) => p!(out, { n.number() }),713 Self::ExprArray(a) => {714 p!(out, str("[") >i nl);715 for el in a.exprs() {716 p!(out, {el} str(",") nl);717 }718 p!(out, <i str("]"));719 }720 Self::ExprObject(obj) => {721 p!(out, { obj.obj_body() });722 }723 Self::ExprArrayComp(arr) => {724 p!(out, str("[") {arr.expr()});725 for spec in arr.comp_specs() {726 p!(out, str(" ") {spec});727 }728 p!(out, str("]"));729 }730 Self::ExprImport(v) => {731 p!(out, {v.import_kind()} str(" ") {v.text()});732 }733 Self::ExprVar(n) => p!(out, { n.name() }),734 // Self::ExprLocal(l) => {735 // }736 Self::ExprIfThenElse(ite) => {737 p!(out, str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});738 if ite.else_kw_token().is_some() || ite.else_().is_some() {739 p!(out, str(" else ") {ite.else_().map(|t| t.expr())});740 }741 }742 Self::ExprFunction(f) => p!(out, str("function") {f.params_desc()} nl {f.expr()}),743 // Self::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),744 Self::ExprError(e) => p!(out, str("error ") {e.expr()}),745 Self::ExprLiteral(l) => {746 p!(out, { l.literal() });747 }748 }749 }750}751752impl Printable for SourceFile {753 fn print(&self, out: &mut PrintItems) {754 let before = trivia_before(755 self.syntax().clone(),756 self.expr()757 .map(|e| e.syntax().clone())758 .map(Into::into)759 .as_ref(),760 );761 let after = trivia_after(762 self.syntax().clone(),763 self.expr()764 .map(|e| e.syntax().clone())765 .map(Into::into)766 .as_ref(),767 );768 format_comments(&before, CommentLocation::AboveItem, out);769 p!(out, {self.expr()} nl);770 format_comments(&after, CommentLocation::EndOfItems, out);771 }772}773774pub struct FormatOptions {775 // 0 for hard tabs776 pub indent: u8,777}778pub fn format(input: &str, opts: &FormatOptions) -> Result<String, SnippetBuilder> {779 let (parsed, errors) = jrsonnet_rowan_parser::parse(input);780 if !errors.is_empty() {781 let mut builder = hi_doc::SnippetBuilder::new(input);782 for error in errors {783 builder784 .error(hi_doc::Text::fragment(785 format!("{:?}", error.error),786 Formatting::default(),787 ))788 .range(789 error.range.start().into()790 ..=(usize::from(error.range.end()) - 1).max(error.range.start().into()),791 )792 .build();793 }794 // let snippet = builder.build();795 return Err(builder);796 // It is possible to recover from this failure, but the output may be broken, as formatter is free to skip797 // ERROR rowan nodes.798 // Recovery needs to be enabled for LSP, though.799 }800 Ok(dprint_core::formatting::format(801 || {802 let mut out = PrintItems::new();803 parsed.print(&mut out);804 out805 },806 PrintOptions {807 indent_width: if opts.indent == 0 {808 // Reasonable max length for both 2 and 4 space sized tabs.809 3810 } else {811 opts.indent812 },813 max_width: 100,814 use_tabs: opts.indent == 0,815 new_line_text: "\n",816 },817 ))818}crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__args.snapdiffbeforeafterboth--- a/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__args.snap
+++ /dev/null
@@ -1,36 +0,0 @@
----
-source: crates/jrsonnet-formatter/src/tests.rs
-expression: "reformat(indoc!(\"\n\t\t\t{\n\t\t\t\tshort: aaa(1,2,3,4,5),\n\t\t\t\tlong: bbb(123123123123123123123,12312312321123123123,123123123123312123123,123123123123123123312,123123123123312321123),\n\t\t\t\tshort_in_long: bbb(aaa(1,2,3,4,5), 123123123123123123123,12312312321123123123,123123123123312123123,123123123123123123312,123123123123312321123),\n\t\t\t\tlong_in_short: aaa(1,2,3,4,5,bbb(123123123123123123123,12312312321123123123,123123123123312123123,123123123123123123312,123123123123312321123)),\n\t\t\t}\n\t\t\"))"
----
-{
- short: aaa(1, 2, 3, 4, 5),
- long: bbb(
- 123123123123123123123,
- 12312312321123123123,
- 123123123123312123123,
- 123123123123123123312,
- 123123123123312321123,
- ),
- short_in_long: bbb(
- aaa(1, 2, 3, 4, 5),
- 123123123123123123123,
- 12312312321123123123,
- 123123123123312123123,
- 123123123123123123312,
- 123123123123312321123,
- ),
- long_in_short: aaa(
- 1,
- 2,
- 3,
- 4,
- 5,
- bbb(
- 123123123123123123123,
- 12312312321123123123,
- 123123123123312123123,
- 123123123123123123312,
- 123123123123312321123,
- ),
- ),
-}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__asserts.snapdiffbeforeafterboth--- a/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__asserts.snap
+++ /dev/null
@@ -1,9 +0,0 @@
----
-source: crates/jrsonnet-formatter/src/tests.rs
-expression: "reformat(indoc!(\"\n\t\t\t{\n\t\t\t\tassert 1 > 0 : 'one should be greater than zero',\n\t\t\t\tassert true,\n\t\t\t\tvalue: 42,\n\t\t\t}\n\t\t\"))"
----
-{
- assert 1 > 0: 'one should be greater than zero',
- assert true,
- value: 42,
-}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__complex_comments.snapdiffbeforeafterboth--- a/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__complex_comments.snap
+++ /dev/null
@@ -1,62 +0,0 @@
----
-source: crates/jrsonnet-formatter/src/tests.rs
-expression: "reformat(indoc!(\"{\n\t\t comments: {\n\t\t\t_: '',\n\t\t\t// Plain comment\n\t\t\ta: '',\n\n\t\t\t# Plain comment with empty line before\n\t\t\tb: '',\n\t\t\t/*Single-line multiline comment\n\n\t\t\t*/\n\t\t\tc: '',\n\n\t\t\t/**Single-line multiline doc comment\n\n\t\t\t*/\n\t\t\tc: '',\n\n\t\t\t/**Multiline doc\n\t\t\tComment\n\t\t\t*/\n\t\t\tc: '',\n\n\t\t\t/*\n\n\tMulti-line\n\n\tcomment\n\t\t\t*/\n\t\t\td: '',\n\n\t\t\te: '', // Inline comment\n\n\t\t\tk: '',\n\n\t\t\t// Text after everything\n\t\t },\n\t\t comments2: {\n\t\t\tk: '',\n\t\t\t// Text after everything, but no newline above\n\t\t },\n spacing: {\n a: '',\n\n b: '',\n },\n noSpacing: {\n a: '',\n b: '',\n },\n\n\t\t\t smallObjectWithEnding: {/*Ending comment*/},\n\t\t\t smallObjectWithFieldAndEnding: {a: 11/*Ending comment*/},\n\t\t\t smallObjectWithFieldAndEnding2: {/*Start*/a: 11/*Ending comment*/},\n }\"))"
----
-{
- comments: {
- _: '',
- // Plain comment
- a: '',
-
- # Plain comment with empty line before
- b: '',
- /* Single-line multiline comment */
- c: '',
-
- /**
- * Single-line multiline doc comment
- */
- c: '',
-
- /**
- * Multiline doc
- * Comment
- */
- c: '',
-
- /*
- Multi-line
-
- comment
- */
- d: '',
-
- e: '', // Inline comment
-
- k: '',
-
- // Text after everything
- },
- comments2: {
- k: '',
- // Text after everything, but no newline above
- },
- spacing: {
- a: '',
-
- b: '',
- },
- noSpacing: {
- a: '',
- b: '',
- },
-
- smallObjectWithEnding: {
- /* Ending comment */
- },
- smallObjectWithFieldAndEnding: { a: 11 /* Ending comment */ },
- smallObjectWithFieldAndEnding2: {
- /* Start */
- a: 11, /* Ending comment */
- },
-}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__complex_nested.snapdiffbeforeafterboth--- a/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__complex_nested.snap
+++ /dev/null
@@ -1,41 +0,0 @@
----
-source: crates/jrsonnet-formatter/src/tests.rs
-expression: "reformat(indoc!(\"\n\t\t\t{\n\t\t\t\tkubernetes: {\n\t\t\t\t deployment: {\n\t\t\t\t\tapiVersion: 'apps/v1',\n\t\t\t\t\tkind: 'Deployment',\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t name: 'myapp',\n\t\t\t\t\t labels: { app: 'myapp', version: 'v1' },\n\t\t\t\t\t},\n\t\t\t\t\tspec: {\n\t\t\t\t\t replicas: 3,\n\t\t\t\t\t selector: { matchLabels: { app: 'myapp' } },\n\t\t\t\t\t template: {\n\t\t\t\t\t\t metadata: { labels: { app: 'myapp' } },\n\t\t\t\t\t\t spec: {\n\t\t\t\t\t\t\tcontainers: [\n\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t name: 'myapp',\n\t\t\t\t\t\t\t\t image: 'myapp:latest',\n\t\t\t\t\t\t\t\t ports: [{ containerPort: 8080 }],\n\t\t\t\t\t\t\t\t env: [\n\t\t\t\t\t\t\t\t\t{ name: 'FOO', value: 'bar' },\n\t\t\t\t\t\t\t\t\t{ name: 'BAZ', valueFrom: { secretKeyRef: { name: 'mysecret', key: 'password' } } },\n\t\t\t\t\t\t\t\t ],\n\t\t\t\t\t\t\t },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t },\n\t\t\t\t\t },\n\t\t\t\t\t},\n\t\t\t\t },\n\t\t\t },\n\t\t\t}\n\t\t\"))"
----
-{
- kubernetes: {
- deployment: {
- apiVersion: 'apps/v1',
- kind: 'Deployment',
- metadata: {
- name: 'myapp',
- labels: { app: 'myapp', version: 'v1' },
- },
- spec: {
- replicas: 3,
- selector: { matchLabels: { app: 'myapp' } },
- template: {
- metadata: { labels: { app: 'myapp' } },
- spec: {
- containers: [
- {
- name: 'myapp',
- image: 'myapp:latest',
- ports: [
- { containerPort: 8080 },
- ],
- env: [
- { name: 'FOO', value: 'bar' },
- {
- name: 'BAZ',
- valueFrom: { secretKeyRef: { name: 'mysecret', key: 'password' } },
- },
- ],
- },
- ],
- },
- },
- },
- },
- },
-}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__self_super.snapdiffbeforeafterboth--- a/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__self_super.snap
+++ /dev/null
@@ -1,13 +0,0 @@
----
-source: crates/jrsonnet-formatter/src/tests.rs
-expression: "reformat(indoc!(\"\n\t\t\tlocal base = {\n\t\t\t foo: 'bar',\n\t\t\t method():: self.foo,\n\t\t\t};\n\n\t\t\tbase {\n\t\t\t foo: super.foo + '-extended',\n\t\t\t result: self.method(),\n\t\t\t}\n\t\t\"))"
----
-local base = {
- foo: 'bar',
- method(
- ):: self.foo,
-};
-base {
- foo: super.foo + '-extended',
- result: self.method(),
-}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@args.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@args.jsonnet.snap
@@ -0,0 +1,37 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/args.jsonnet
+---
+{
+ short: aaa(1, 2, 3, 4, 5),
+ long: bbb(
+ 123123123123123123123,
+ 12312312321123123123,
+ 123123123123312123123,
+ 123123123123123123312,
+ 123123123123312321123,
+ ),
+ short_in_long: bbb(
+ aaa(1, 2, 3, 4, 5),
+ 123123123123123123123,
+ 12312312321123123123,
+ 123123123123312123123,
+ 123123123123123123312,
+ 123123123123312321123,
+ ),
+ long_in_short: aaa(
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ bbb(
+ 123123123123123123123,
+ 12312312321123123123,
+ 123123123123312123123,
+ 123123123123123123312,
+ 123123123123312321123,
+ ),
+ ),
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@asserts.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@asserts.jsonnet.snap
@@ -0,0 +1,10 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/asserts.jsonnet
+---
+{
+ assert 1 > 0: 'one should be greater than zero',
+ assert true,
+ value: 42,
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@basic_array.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@basic_array.jsonnet.snap
@@ -0,0 +1,12 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/basic_array.jsonnet
+---
+[
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+]
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@basic_object.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@basic_object.jsonnet.snap
@@ -0,0 +1,6 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/basic_object.jsonnet
+---
+{ foo: 'bar', baz: 'qux', nested: { a: 1, b: 2 } }
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@comments.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@comments.jsonnet.snap
@@ -0,0 +1,16 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/comments.jsonnet
+---
+// File header comment
+{
+ // Comment above field
+ foo: 'bar',
+ /* Block comment */
+ baz: 'qux',
+ nested: {
+ // Hash comment
+ value: 42,
+ },
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@complex_comments.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@complex_comments.jsonnet.snap
@@ -0,0 +1,63 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/complex_comments.jsonnet
+---
+{
+ comments: {
+ _: '',
+ // Plain comment
+ a: '',
+
+ # Plain comment with empty line before
+ b: '',
+ /* Single-line multiline comment */
+ c: '',
+
+ /**
+ * Single-line multiline doc comment
+ */
+ c: '',
+
+ /**
+ * Multiline doc
+ * Comment
+ */
+ c: '',
+
+ /*
+ Multi-line
+
+ comment
+ */
+ d: '',
+
+ e: '', // Inline comment
+
+ k: '',
+
+ // Text after everything
+ },
+ comments2: {
+ k: '',
+ // Text after everything, but no newline above
+ },
+ spacing: {
+ a: '',
+
+ b: '',
+ },
+ noSpacing: {
+ a: '',
+ b: '',
+ },
+
+ smallObjectWithEnding: {
+ /* Ending comment */
+ },
+ smallObjectWithFieldAndEnding: { a: 11 /* Ending comment */ },
+ smallObjectWithFieldAndEnding2: {
+ /* Start */
+ a: 11, /* Ending comment */
+ },
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@complex_nested.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@complex_nested.jsonnet.snap
@@ -0,0 +1,42 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/complex_nested.jsonnet
+---
+{
+ kubernetes: {
+ deployment: {
+ apiVersion: 'apps/v1',
+ kind: 'Deployment',
+ metadata: {
+ name: 'myapp',
+ labels: { app: 'myapp', version: 'v1' },
+ },
+ spec: {
+ replicas: 3,
+ selector: { matchLabels: { app: 'myapp' } },
+ template: {
+ metadata: { labels: { app: 'myapp' } },
+ spec: {
+ containers: [
+ {
+ name: 'myapp',
+ image: 'myapp:latest',
+ ports: [
+ { containerPort: 8080 },
+ ],
+ env: [
+ { name: 'FOO', value: 'bar' },
+ {
+ name: 'BAZ',
+ valueFrom: { secretKeyRef: { name: 'mysecret', key: 'password' } },
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ },
+ },
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@comprehensions.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@comprehensions.jsonnet.snap
@@ -0,0 +1,29 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/comprehensions.jsonnet
+---
+{
+ arr: [x for x in [
+ 1,
+ 2,
+ 3,
+ ]],
+ filtered: [x for x in [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ ] if x > 2],
+ obj: {
+ [k]: v,
+ for k in [
+ 'a',
+ 'b',
+ ]for v in [
+ 1,
+ 2,
+ ]
+ },
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@conditionals.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@conditionals.jsonnet.snap
@@ -0,0 +1,9 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/conditionals.jsonnet
+---
+{
+ simple: if true then 'yes' else 'no',
+ nested: if 1 > 0 then if 2 > 1 then 'a' else 'b' else 'c',
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@functions.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@functions.jsonnet.snap
@@ -0,0 +1,20 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/functions.jsonnet
+---
+{
+ simple(
+ x,
+ ):: x * 2,
+ with_default(
+ x,
+ y = 10,
+ ):: x + y,
+ multiline(
+ a,
+ b,
+ c,
+ ):: a + b + c,
+ called: self.simple(5),
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@imports.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@imports.jsonnet.snap
@@ -0,0 +1,11 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/imports.jsonnet
+---
+local a = import 'a.libsonnet';
+local m = import 'm.libsonnet';
+local z = import 'z.libsonnet';
+{
+ result: a + m + z,
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@local_vars.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@local_vars.jsonnet.snap
@@ -0,0 +1,12 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/local_vars.jsonnet
+---
+local x = 10;
+local y = 20;
+local sum = x + y;
+{
+ local inner = 5,
+ result: sum + inner,
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@operators.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@operators.jsonnet.snap
@@ -0,0 +1,18 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/operators.jsonnet
+---
+{
+ arithmetic: 1 + 2 * 3 - 4 / 2,
+ comparison: 1 < 2 && 3 > 2 || false,
+ string_concat: 'hello' + ' ' + 'world',
+ object_concat: { a: 1 } + { b: 2 },
+ array_concat: [
+ 1,
+ 2,
+ ] + [
+ 3,
+ 4,
+ ],
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@self_super.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@self_super.jsonnet.snap
@@ -0,0 +1,14 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/self_super.jsonnet
+---
+local base = {
+ foo: 'bar',
+ method(
+ ):: self.foo,
+};
+base {
+ foo: super.foo + '-extended',
+ result: self.method(),
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@std_functions.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@std_functions.jsonnet.snap
@@ -0,0 +1,22 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/std_functions.jsonnet
+---
+{
+ length: std.length(
+ [
+ 1,
+ 2,
+ 3,
+ ],
+ ),
+ type: std.type('hello'),
+ format: std.format(
+ 'Hello, %s!',
+ [
+ 'world',
+ ],
+ ),
+ manifest: std.manifestJsonEx({ foo: 'bar' }, ' '),
+}
crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@string_styles.jsonnet.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/snapshots/jrsonnet_formatter__tests__snapshots@string_styles.jsonnet.snap
@@ -0,0 +1,14 @@
+---
+source: crates/jrsonnet-formatter/src/tests.rs
+expression: reformat(&input)
+input_file: crates/jrsonnet-formatter/src/tests/string_styles.jsonnet
+---
+{
+ double_quote: 'hello world',
+ single_quote: 'hello world',
+ escaped: 'line1\nline2',
+ multiline: |||
+ This is a
+ multiline string
+ |||,
+}
crates/jrsonnet-formatter/src/tests.rsdiffbeforeafterboth--- a/crates/jrsonnet-formatter/src/tests.rs
+++ b/crates/jrsonnet-formatter/src/tests.rs
@@ -1,5 +1,10 @@
+#![cfg(test)]
+
+use std::fs;
+
use dprint_core::formatting::{PrintItems, PrintOptions};
use indoc::indoc;
+use insta::{assert_snapshot, glob};
use crate::Printable;
@@ -13,155 +18,18 @@
out
},
PrintOptions {
- indent_width: 2,
+ indent_width: 3,
max_width: 100,
- use_tabs: true,
+ use_tabs: false,
new_line_text: "\n",
},
)
}
#[test]
-fn complex_comments() {
- insta::assert_snapshot!(reformat(indoc!(
- "{
- comments: {
- _: '',
- // Plain comment
- a: '',
-
- # Plain comment with empty line before
- b: '',
- /*Single-line multiline comment
-
- */
- c: '',
-
- /**Single-line multiline doc comment
-
- */
- c: '',
-
- /**Multiline doc
- Comment
- */
- c: '',
-
- /*
-
- Multi-line
-
- comment
- */
- d: '',
-
- e: '', // Inline comment
-
- k: '',
-
- // Text after everything
- },
- comments2: {
- k: '',
- // Text after everything, but no newline above
- },
- spacing: {
- a: '',
-
- b: '',
- },
- noSpacing: {
- a: '',
- b: '',
- },
-
- smallObjectWithEnding: {/*Ending comment*/},
- smallObjectWithFieldAndEnding: {a: 11/*Ending comment*/},
- smallObjectWithFieldAndEnding2: {/*Start*/a: 11/*Ending comment*/},
- }"
- )));
-}
-
-#[test]
-fn args() {
- insta::assert_snapshot!(reformat(indoc!(
- "
- {
- short: aaa(1,2,3,4,5),
- long: bbb(123123123123123123123,12312312321123123123,123123123123312123123,123123123123123123312,123123123123312321123),
- short_in_long: bbb(aaa(1,2,3,4,5), 123123123123123123123,12312312321123123123,123123123123312123123,123123123123123123312,123123123123312321123),
- long_in_short: aaa(1,2,3,4,5,bbb(123123123123123123123,12312312321123123123,123123123123312123123,123123123123123123312,123123123123312321123)),
- }
- "
- )));
-}
-
-#[test]
-fn asserts() {
- insta::assert_snapshot!(reformat(indoc!(
- "
- {
- assert 1 > 0 : 'one should be greater than zero',
- assert true,
- value: 42,
- }
- "
- )));
-}
-
-#[test]
-fn complex_nested() {
- insta::assert_snapshot!(reformat(indoc!(
- "
- {
- kubernetes: {
- deployment: {
- apiVersion: 'apps/v1',
- kind: 'Deployment',
- metadata: {
- name: 'myapp',
- labels: { app: 'myapp', version: 'v1' },
- },
- spec: {
- replicas: 3,
- selector: { matchLabels: { app: 'myapp' } },
- template: {
- metadata: { labels: { app: 'myapp' } },
- spec: {
- containers: [
- {
- name: 'myapp',
- image: 'myapp:latest',
- ports: [{ containerPort: 8080 }],
- env: [
- { name: 'FOO', value: 'bar' },
- { name: 'BAZ', valueFrom: { secretKeyRef: { name: 'mysecret', key: 'password' } } },
- ],
- },
- ],
- },
- },
- },
- },
- },
- }
- "
- )));
-}
-
-#[test]
-fn self_super() {
- insta::assert_snapshot!(reformat(indoc!(
- "
- local base = {
- foo: 'bar',
- method():: self.foo,
- };
-
- base {
- foo: super.foo + '-extended',
- result: self.method(),
- }
- "
- )));
+fn snapshots() {
+ glob!("tests/*.jsonnet", |path| {
+ let input = fs::read_to_string(path).expect("read test file");
+ assert_snapshot!(reformat(&input));
+ });
}
crates/jrsonnet-formatter/src/tests/args.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/args.jsonnet
@@ -0,0 +1,7 @@
+
+ {
+ short: aaa(1,2,3,4,5),
+ long: bbb(123123123123123123123,12312312321123123123,123123123123312123123,123123123123123123312,123123123123312321123),
+ short_in_long: bbb(aaa(1,2,3,4,5), 123123123123123123123,12312312321123123123,123123123123312123123,123123123123123123312,123123123123312321123),
+ long_in_short: aaa(1,2,3,4,5,bbb(123123123123123123123,12312312321123123123,123123123123312123123,123123123123123123312,123123123123312321123)),
+ }
crates/jrsonnet-formatter/src/tests/asserts.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/asserts.jsonnet
@@ -0,0 +1,6 @@
+
+ {
+ assert 1 > 0 : 'one should be greater than zero',
+ assert true,
+ value: 42,
+ }
crates/jrsonnet-formatter/src/tests/basic_array.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/basic_array.jsonnet
@@ -0,0 +1 @@
+[1, 2, 3, 4, 5]
crates/jrsonnet-formatter/src/tests/basic_object.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/basic_object.jsonnet
@@ -0,0 +1 @@
+{ foo: 'bar', baz: 'qux', nested: { a: 1, b: 2 } }
crates/jrsonnet-formatter/src/tests/comments.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/comments.jsonnet
@@ -0,0 +1,11 @@
+// File header comment
+{
+ // Comment above field
+ foo: 'bar',
+ /* Block comment */
+ baz: 'qux',
+ nested: {
+ // Hash comment
+ value: 42,
+ },
+}
crates/jrsonnet-formatter/src/tests/complex_comments.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/complex_comments.jsonnet
@@ -0,0 +1,55 @@
+{
+ comments: {
+ _: '',
+ // Plain comment
+ a: '',
+
+ # Plain comment with empty line before
+ b: '',
+ /*Single-line multiline comment
+
+ */
+ c: '',
+
+ /**Single-line multiline doc comment
+
+ */
+ c: '',
+
+ /**Multiline doc
+ Comment
+ */
+ c: '',
+
+ /*
+
+ Multi-line
+
+ comment
+ */
+ d: '',
+
+ e: '', // Inline comment
+
+ k: '',
+
+ // Text after everything
+ },
+ comments2: {
+ k: '',
+ // Text after everything, but no newline above
+ },
+ spacing: {
+ a: '',
+
+ b: '',
+ },
+ noSpacing: {
+ a: '',
+ b: '',
+ },
+
+ smallObjectWithEnding: {/*Ending comment*/},
+ smallObjectWithFieldAndEnding: {a: 11/*Ending comment*/},
+ smallObjectWithFieldAndEnding2: {/*Start*/a: 11/*Ending comment*/},
+ }
crates/jrsonnet-formatter/src/tests/complex_nested.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/complex_nested.jsonnet
@@ -0,0 +1,33 @@
+
+ {
+ kubernetes: {
+ deployment: {
+ apiVersion: 'apps/v1',
+ kind: 'Deployment',
+ metadata: {
+ name: 'myapp',
+ labels: { app: 'myapp', version: 'v1' },
+ },
+ spec: {
+ replicas: 3,
+ selector: { matchLabels: { app: 'myapp' } },
+ template: {
+ metadata: { labels: { app: 'myapp' } },
+ spec: {
+ containers: [
+ {
+ name: 'myapp',
+ image: 'myapp:latest',
+ ports: [{ containerPort: 8080 }],
+ env: [
+ { name: 'FOO', value: 'bar' },
+ { name: 'BAZ', valueFrom: { secretKeyRef: { name: 'mysecret', key: 'password' } } },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ },
+ },
+ }
crates/jrsonnet-formatter/src/tests/comprehensions.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/comprehensions.jsonnet
@@ -0,0 +1,5 @@
+{
+ arr: [x for x in [1, 2, 3]],
+ filtered: [x for x in [1, 2, 3, 4, 5] if x > 2],
+ obj: { [k]: v for k in ['a', 'b'] for v in [1, 2] },
+}
crates/jrsonnet-formatter/src/tests/conditionals.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/conditionals.jsonnet
@@ -0,0 +1,7 @@
+{
+ simple: if true then 'yes' else 'no',
+ nested: if 1 > 0 then
+ if 2 > 1 then 'a' else 'b'
+ else
+ 'c',
+}
crates/jrsonnet-formatter/src/tests/functions.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/functions.jsonnet
@@ -0,0 +1,10 @@
+{
+ simple(x):: x * 2,
+ with_default(x, y=10):: x + y,
+ multiline(
+ a,
+ b,
+ c,
+ ):: a + b + c,
+ called: self.simple(5),
+}
crates/jrsonnet-formatter/src/tests/imports.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/imports.jsonnet
@@ -0,0 +1,7 @@
+local a = import 'a.libsonnet';
+local m = import 'm.libsonnet';
+local z = import 'z.libsonnet';
+
+{
+ result: a + m + z,
+}
crates/jrsonnet-formatter/src/tests/local_vars.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/local_vars.jsonnet
@@ -0,0 +1,8 @@
+local x = 10;
+local y = 20;
+local sum = x + y;
+
+{
+ local inner = 5,
+ result: sum + inner,
+}
crates/jrsonnet-formatter/src/tests/operators.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/operators.jsonnet
@@ -0,0 +1,7 @@
+{
+ arithmetic: 1 + 2 * 3 - 4 / 2,
+ comparison: 1 < 2 && 3 > 2 || false,
+ string_concat: 'hello' + ' ' + 'world',
+ object_concat: { a: 1 } + { b: 2 },
+ array_concat: [1, 2] + [3, 4],
+}
crates/jrsonnet-formatter/src/tests/self_super.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/self_super.jsonnet
@@ -0,0 +1,9 @@
+local base = {
+ foo: 'bar',
+ method():: self.foo,
+};
+
+base {
+ foo: super.foo + '-extended',
+ result: self.method(),
+}
crates/jrsonnet-formatter/src/tests/std_functions.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/std_functions.jsonnet
@@ -0,0 +1,6 @@
+{
+ length: std.length([1, 2, 3]),
+ type: std.type('hello'),
+ format: std.format('Hello, %s!', ['world']),
+ manifest: std.manifestJsonEx({ foo: 'bar' }, ' '),
+}
crates/jrsonnet-formatter/src/tests/string_styles.jsonnetdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-formatter/src/tests/string_styles.jsonnet
@@ -0,0 +1,9 @@
+{
+ double_quote: 'hello world',
+ single_quote: 'hello world',
+ escaped: 'line1\nline2',
+ multiline: |||
+ This is a
+ multiline string
+ |||,
+}
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__plain_call.snapdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__plain_call.snap
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__plain_call.snap
@@ -5,49 +5,49 @@
SOURCE_FILE@0..37
EXPR@0..36
EXPR_BINARY@0..36
- EXPR@0..3
+ EXPR@0..31
EXPR_VAR@0..3
NAME@0..3
IDENT@0..3 "std"
- SUFFIX_INDEX@3..10
- DOT@3..4 "."
- NAME@4..10
- IDENT@4..10 "substr"
- SUFFIX_APPLY@10..31
- ARGS_DESC@10..31
- L_PAREN@10..11 "("
- ARG@11..12
- EXPR@11..12
- EXPR_VAR@11..12
- NAME@11..12
- IDENT@11..12 "a"
- COMMA@12..13 ","
- WHITESPACE@13..14 " "
- ARG@14..15
- EXPR@14..15
- EXPR_NUMBER@14..15
- FLOAT@14..15 "0"
- COMMA@15..16 ","
- WHITESPACE@16..17 " "
- ARG@17..30
- EXPR@17..30
- EXPR_VAR@17..20
- NAME@17..20
- IDENT@17..20 "std"
- SUFFIX_INDEX@20..27
- DOT@20..21 "."
- NAME@21..27
- IDENT@21..27 "length"
- SUFFIX_APPLY@27..30
- ARGS_DESC@27..30
- L_PAREN@27..28 "("
- ARG@28..29
- EXPR@28..29
- EXPR_VAR@28..29
- NAME@28..29
- IDENT@28..29 "b"
- R_PAREN@29..30 ")"
- R_PAREN@30..31 ")"
+ SUFFIX_INDEX@3..10
+ DOT@3..4 "."
+ NAME@4..10
+ IDENT@4..10 "substr"
+ SUFFIX_APPLY@10..31
+ ARGS_DESC@10..31
+ L_PAREN@10..11 "("
+ ARG@11..12
+ EXPR@11..12
+ EXPR_VAR@11..12
+ NAME@11..12
+ IDENT@11..12 "a"
+ COMMA@12..13 ","
+ WHITESPACE@13..14 " "
+ ARG@14..15
+ EXPR@14..15
+ EXPR_NUMBER@14..15
+ FLOAT@14..15 "0"
+ COMMA@15..16 ","
+ WHITESPACE@16..17 " "
+ ARG@17..30
+ EXPR@17..30
+ EXPR_VAR@17..20
+ NAME@17..20
+ IDENT@17..20 "std"
+ SUFFIX_INDEX@20..27
+ DOT@20..21 "."
+ NAME@21..27
+ IDENT@21..27 "length"
+ SUFFIX_APPLY@27..30
+ ARGS_DESC@27..30
+ L_PAREN@27..28 "("
+ ARG@28..29
+ EXPR@28..29
+ EXPR_VAR@28..29
+ NAME@28..29
+ IDENT@28..29 "b"
+ R_PAREN@29..30 ")"
+ R_PAREN@30..31 ")"
WHITESPACE@31..32 " "
EQ@32..34 "=="
WHITESPACE@34..35 " "
crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__string_block_trim.snapdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__string_block_trim.snap
@@ -0,0 +1,9 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "|||-\n\tTrimmed text block\n|||\n"
+---
+SOURCE_FILE@0..29
+ EXPR@0..28
+ EXPR_STRING@0..28
+ STRING_BLOCK@0..28 "|||-\n\tTrimmed text bl ..."
+ WHITESPACE@28..29 "\n"
crates/jrsonnet-rowan-parser/src/string_block.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/string_block.rs
+++ b/crates/jrsonnet-rowan-parser/src/string_block.rs
@@ -52,6 +52,14 @@
self.rest().chars().next()
}
+ fn eat_if(&mut self, f: impl Fn(char) -> bool) -> usize {
+ if self.peek().map(f).unwrap_or(false) {
+ self.index += 1;
+ return 1;
+ }
+ 0
+ }
+
fn eat_while(&mut self, f: impl Fn(char) -> bool) -> usize {
if self.index == self.source.len() {
return 0;
@@ -133,6 +141,8 @@
offset: lex.span().end,
};
+ ctx.eat_if(|v| v == '-');
+
// Skip whitespaces
ctx.eat_while(|r| r == ' ' || r == '\t' || r == '\r');
crates/jrsonnet-rowan-parser/src/tests.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/tests.rs
+++ b/crates/jrsonnet-rowan-parser/src/tests.rs
@@ -55,7 +55,6 @@
let src = indoc::indoc!($test);
let result = process(&src);
insta::assert_snapshot!(stringify!($name), result, src);
-
}
)+};
}
@@ -204,6 +203,12 @@
super_nesting => r#"
super.a + super.b
"#
+
+ string_block_trim => r#"
+ |||-
+ Trimmed text block
+ |||
+ "#
);
#[test]