git.delta.rocks / jrsonnet / refs/commits / e17e60d35bf5

difftreelog

feat(fmt) try to use ass-stroke

Yaroslav Bolyukin2023-09-04parent: #fa1839d.patch.diff
in: master

4 files changed

modifiedcmds/jrsonnet-fmt/Cargo.tomldiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/Cargo.toml
+++ b/cmds/jrsonnet-fmt/Cargo.toml
@@ -8,3 +8,4 @@
 jrsonnet-rowan-parser.workspace = true
 insta = "1.15"
 indoc = "1.0"
+ass-stroke = { path = "/home/lach/build/ass-stroke/crates/ass-stroke", version = "0.1.0" }
modifiedcmds/jrsonnet-fmt/src/children.rsdiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/src/children.rs
+++ b/cmds/jrsonnet-fmt/src/children.rs
@@ -267,3 +267,8 @@
 	pub should_start_with_newline: bool,
 	pub trivia: ChildTrivia,
 }
+impl EndingComments {
+	pub fn is_empty(&self) -> bool {
+		!self.should_start_with_newline && self.trivia.is_empty()
+	}
+}
modifiedcmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth
before · cmds/jrsonnet-fmt/src/main.rs
1use std::any::type_name;23use children::{children_between, trivia_before};4use dprint_core::formatting::{PrintItems, PrintOptions};5use jrsonnet_rowan_parser::{6	nodes::{7		ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,8		DestructRest, Expr, FieldName, ForSpec, IfSpec, ImportKind, LhsExpr, Literal, Member, Name,9		Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Text, UnaryOperator,10		Visibility, VisibilityKind,11	},12	rowan::NodeOrToken,13	AstNode, AstToken, SyntaxToken,14};1516use crate::{17	children::{trivia_after, trivia_between},18	comments::{format_comments, CommentLocation},19};2021mod children;22mod comments;23#[cfg(test)]24mod tests;2526pub trait Printable {27	fn print(&self) -> PrintItems;28}2930macro_rules! pi {31	(@i; $($t:tt)*) => {{32		#[allow(unused_mut)]33		let mut o = dprint_core::formatting::PrintItems::new();34		pi!(@s; o: $($t)*);35		o36	}};37	(@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{38		$o.push_str($e);39		pi!(@s; $o: $($t)*);40	}};41	(@s; $o:ident: string($e:expr $(,)?) $($t:tt)*) => {{42		$o.push_string($e);43		pi!(@s; $o: $($t)*);44	}};45	(@s; $o:ident: nl $($t:tt)*) => {{46		$o.push_signal(dprint_core::formatting::Signal::NewLine);47		pi!(@s; $o: $($t)*);48	}};49	(@s; $o:ident: tab $($t:tt)*) => {{50		$o.push_signal(dprint_core::formatting::Signal::Tab);51		pi!(@s; $o: $($t)*);52	}};53	(@s; $o:ident: >i $($t:tt)*) => {{54		$o.push_signal(dprint_core::formatting::Signal::StartIndent);55		pi!(@s; $o: $($t)*);56	}};57	(@s; $o:ident: <i $($t:tt)*) => {{58		$o.push_signal(dprint_core::formatting::Signal::FinishIndent);59		pi!(@s; $o: $($t)*);60	}};61	(@s; $o:ident: {$expr:expr} $($t:tt)*) => {{62		$o.extend($expr.print());63		pi!(@s; $o: $($t)*);64	}};65	(@s; $o:ident: items($expr:expr) $($t:tt)*) => {{66		$o.extend($expr);67		pi!(@s; $o: $($t)*);68	}};69	(@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{70		if $e {71			pi!(@s; $o: $($then)*);72		}73		pi!(@s; $o: $($t)*);74	}};75	(@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{76		if $e {77			pi!(@s; $o: $($then)*);78		} else {79			pi!(@s; $o: $($else)*);80		}81		pi!(@s; $o: $($t)*);82	}};83	(@s; $i:ident:) => {}84}85macro_rules! p {86	(new: $($t:tt)*) => {87		pi!(@i; $($t)*)88	};89	($o:ident: $($t:tt)*) => {90		pi!(@s; $o: $($t)*)91	};92}93pub(crate) use p;94pub(crate) use pi;9596impl<P> Printable for Option<P>97where98	P: Printable,99{100	fn print(&self) -> PrintItems {101		if let Some(v) = self {102			v.print()103		} else {104			p!(new: string(105				format!(106					"/*missing {}*/",107					type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")108				),109			))110		}111	}112}113114impl Printable for SyntaxToken {115	fn print(&self) -> PrintItems {116		p!(new: string(self.to_string()))117	}118}119120impl Printable for Text {121	fn print(&self) -> PrintItems {122		p!(new: string(format!("{}", self)))123	}124}125impl Printable for Number {126	fn print(&self) -> PrintItems {127		p!(new: string(format!("{}", self)))128	}129}130131impl Printable for Name {132	fn print(&self) -> PrintItems {133		p!(new: {self.ident_lit()})134	}135}136137impl Printable for DestructRest {138	fn print(&self) -> PrintItems {139		let mut pi = p!(new: str("..."));140		if let Some(name) = self.into() {141			p!(pi: {name});142		}143		pi144	}145}146147impl Printable for Destruct {148	fn print(&self) -> PrintItems {149		let mut pi = p!(new:);150		match self {151			Destruct::DestructFull(f) => {152				p!(pi: {f.name()})153			}154			Destruct::DestructSkip(_) => p!(pi: str("?")),155			Destruct::DestructArray(a) => {156				p!(pi: str("[") >i nl);157				for el in a.destruct_array_parts() {158					match el {159						DestructArrayPart::DestructArrayElement(e) => {160							p!(pi: {e.destruct()} str(",") nl)161						}162						DestructArrayPart::DestructRest(d) => {163							p!(pi: {d} str(",") nl)164						}165					}166				}167				p!(pi: <i str("]"));168			}169			Destruct::DestructObject(o) => {170				p!(pi: str("{") >i nl);171				for item in o.destruct_object_fields() {172					p!(pi: {item.field()});173					if let Some(des) = item.destruct() {174						p!(pi: str(": ") {des})175					}176					if let Some(def) = item.expr() {177						p!(pi: str(" = ") {def});178					}179					p!(pi: str(",") nl);180				}181				if let Some(rest) = o.destruct_rest() {182					p!(pi: {rest} nl)183				}184				p!(pi: <i str("}"));185			}186		}187		pi188	}189}190191impl Printable for FieldName {192	fn print(&self) -> PrintItems {193		match self {194			FieldName::FieldNameFixed(f) => {195				if let Some(id) = f.id() {196					p!(new: {id})197				} else if let Some(str) = f.text() {198					p!(new: {str})199				} else {200					p!(new: str("/*missing FieldName*/"))201				}202			}203			FieldName::FieldNameDynamic(d) => {204				p!(new: str("[") {d.expr()} str("]"))205			}206		}207	}208}209210impl Printable for Visibility {211	fn print(&self) -> PrintItems {212		p!(new: string(self.to_string()))213	}214}215216impl Printable for ObjLocal {217	fn print(&self) -> PrintItems {218		p!(new: str("local ") {self.bind()})219	}220}221222impl Printable for Assertion {223	fn print(&self) -> PrintItems {224		let mut pi = p!(new: str("assert ") {self.condition()});225		if self.colon_token().is_some() || self.message().is_some() {226			p!(pi: str(": ") {self.message()})227		}228		pi229	}230}231232impl Printable for ParamsDesc {233	fn print(&self) -> PrintItems {234		let mut pi = p!(new: str("(") >i nl);235		for param in self.params() {236			p!(pi: {param.destruct()});237			if param.assign_token().is_some() || param.expr().is_some() {238				p!(pi: str(" = ") {param.expr()})239			}240			p!(pi: str(",") nl)241		}242		p!(pi: <i str(")"));243		pi244	}245}246impl Printable for ArgsDesc {247	fn print(&self) -> PrintItems {248		let mut pi = p!(new: str("(") >i nl);249		for arg in self.args() {250			if arg.name().is_some() || arg.assign_token().is_some() {251				p!(pi: {arg.name()} str(" = "));252			}253			p!(pi: {arg.expr()} str(",") nl)254		}255		p!(pi: <i str(")"));256		pi257	}258}259impl Printable for SliceDesc {260	fn print(&self) -> PrintItems {261		let mut pi = p!(new: str("["));262		if self.from().is_some() {263			p!(pi: {self.from()});264		}265		p!(pi: str(":"));266		if self.end().is_some() {267			p!(pi: {self.end().map(|e|e.expr())})268		}269		// Keep only one : in case if we don't need step270		if self.step().is_some() {271			p!(pi: str(":") {self.step().map(|e|e.expr())});272		}273		p!(pi: str("]"));274		pi275	}276}277278impl Printable for Member {279	fn print(&self) -> PrintItems {280		match self {281			Member::MemberBindStmt(b) => {282				p!(new: {b.obj_local()})283			}284			Member::MemberAssertStmt(ass) => {285				p!(new: {ass.assertion()})286			}287			Member::MemberFieldNormal(n) => {288				p!(new: {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()})289			}290			Member::MemberFieldMethod(_) => todo!(),291		}292	}293}294295impl Printable for ObjBody {296	fn print(&self) -> PrintItems {297		match self {298			ObjBody::ObjBodyComp(l) => {299				let mut pi = p!(new: str("{") >i nl);300				let (children, end_comments) = children_between::<Member>(301					l.syntax().clone(),302					l.l_brace_token().map(Into::into).as_ref(),303					Some(304						&(l.comp_specs()305							.next()306							.expect("at least one spec is defined")307							.syntax()308							.clone())309						.into(),310					),311				);312				for mem in children.into_iter() {313					if mem.should_start_with_newline {314						p!(pi: nl);315					}316					p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));317					p!(pi: {mem.value} str(","));318					p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));319					p!(pi: nl)320				}321322				if end_comments.should_start_with_newline {323					p!(pi: nl);324				}325				p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));326327				let (compspecs, end_comments) = children_between::<CompSpec>(328					l.syntax().clone(),329					l.member_comps()330						.last()331						.map(|m| m.syntax().clone())332						.map(Into::into)333						.or_else(|| l.l_brace_token().map(Into::into))334						.as_ref(),335					l.r_brace_token().map(Into::into).as_ref(),336				);337				for mem in compspecs.into_iter() {338					if mem.should_start_with_newline {339						p!(pi: nl);340					}341					p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));342					p!(pi: {mem.value});343					p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));344					p!(pi: nl)345				}346				if end_comments.should_start_with_newline {347					p!(pi: nl);348				}349				p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));350351				p!(pi: <i str("}"));352				pi353			}354			ObjBody::ObjBodyMemberList(l) => {355				let mut pi = p!(new: str("{") >i nl);356				let (children, end_comments) = children_between::<Member>(357					l.syntax().clone(),358					l.l_brace_token().map(Into::into).as_ref(),359					l.r_brace_token().map(Into::into).as_ref(),360				);361				for mem in children.into_iter() {362					if mem.should_start_with_newline {363						p!(pi: nl);364					}365					p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));366					p!(pi: {mem.value} str(","));367					p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));368					p!(pi: nl)369				}370371				if end_comments.should_start_with_newline {372					p!(pi: nl);373				}374				p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));375				p!(pi: <i str("}"));376				pi377			}378		}379	}380}381impl Printable for UnaryOperator {382	fn print(&self) -> PrintItems {383		p!(new: string(self.text().to_string()))384	}385}386impl Printable for BinaryOperator {387	fn print(&self) -> PrintItems {388		p!(new: string(self.text().to_string()))389	}390}391impl Printable for Bind {392	fn print(&self) -> PrintItems {393		match self {394			Bind::BindDestruct(d) => {395				p!(new: {d.into()} str(" = ") {d.value()})396			}397			Bind::BindFunction(f) => {398				p!(new: str("function") {f.params()} str(" = ") {f.value()})399			}400		}401	}402}403impl Printable for Literal {404	fn print(&self) -> PrintItems {405		p!(new: string(self.syntax().to_string()))406	}407}408impl Printable for ImportKind {409	fn print(&self) -> PrintItems {410		p!(new: string(self.syntax().to_string()))411	}412}413impl Printable for LhsExpr {414	fn print(&self) -> PrintItems {415		p!(new: {self.expr()})416	}417}418impl Printable for ForSpec {419	fn print(&self) -> PrintItems {420		p!(new: str("for ") {self.bind()} str(" in ") {self.expr()})421	}422}423impl Printable for IfSpec {424	fn print(&self) -> PrintItems {425		p!(new: str("if ") {self.expr()})426	}427}428impl Printable for CompSpec {429	fn print(&self) -> PrintItems {430		match self {431			CompSpec::ForSpec(f) => f.print(),432			CompSpec::IfSpec(i) => i.print(),433		}434	}435}436impl Printable for Expr {437	fn print(&self) -> PrintItems {438		match self {439			Expr::ExprBinary(b) => {440				p!(new: {b.lhs()} str(" ") {b.binary_operator()} str(" ") {b.rhs()})441			}442			Expr::ExprUnary(u) => p!(new: {u.unary_operator()} {u.rhs()}),443			Expr::ExprSlice(s) => {444				p!(new: {s.expr()} {s.slice_desc()})445			}446			Expr::ExprIndex(i) => {447				p!(new: {i.expr()} str(".") {i.index()})448			}449			Expr::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),450			Expr::ExprApply(a) => {451				let mut pi = p!(new: {a.expr()} {a.args_desc()});452				if a.tailstrict_kw_token().is_some() {453					p!(pi: str(" tailstrict"));454				}455				pi456			}457			Expr::ExprObjExtend(ex) => {458				p!(new: {ex.lhs_expr()} str(" ") {ex.expr()})459			}460			Expr::ExprParened(p) => {461				p!(new: str("(") {p.expr()} str(")"))462			}463			Expr::ExprString(s) => p!(new: {s.text()}),464			Expr::ExprNumber(n) => p!(new: {n.number()}),465			Expr::ExprArray(a) => {466				let mut pi = p!(new: str("[") >i nl);467				for el in a.exprs() {468					p!(pi: {el} str(",") nl);469				}470				p!(pi: <i str("]"));471				pi472			}473			Expr::ExprObject(o) => {474				p!(new: {o.obj_body()})475			}476			Expr::ExprArrayComp(arr) => {477				let mut pi = p!(new: str("[") {arr.expr()});478				for spec in arr.comp_specs() {479					p!(pi: str(" ") {spec});480				}481				p!(pi: str("]"));482				pi483			}484			Expr::ExprImport(v) => {485				p!(new: {v.import_kind()} str(" ") {v.text()})486			}487			Expr::ExprVar(n) => p!(new: {n.name()}),488			Expr::ExprLocal(l) => {489				let mut pi = p!(new:);490				let (binds, end_comments) = children_between::<Bind>(491					l.syntax().clone(),492					l.local_kw_token().map(Into::into).as_ref(),493					l.semi_token().map(Into::into).as_ref(),494				);495				if binds.len() == 1 {496					let bind = &binds[0];497					p!(pi: items(format_comments(&bind.before_trivia, CommentLocation::AboveItem)));498					p!(pi: str("local ") {bind.value});499				// TODO: keep end_comments, child.inline_trivia somehow, force multiple locals formatting in case of presence?500				} else {501					p!(pi: str("local") >i nl);502					for bind in binds {503						if bind.should_start_with_newline {504							p!(pi: nl);505						}506						p!(pi: items(format_comments(&bind.before_trivia, CommentLocation::AboveItem)));507						p!(pi: {bind.value} str(";"));508						p!(pi: items(format_comments(&bind.inline_trivia, CommentLocation::ItemInline)) nl);509					}510					if end_comments.should_start_with_newline {511						p!(pi: nl)512					}513					p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));514					p!(pi: <i);515				}516				p!(pi: str(";") nl);517518				let expr_comments = trivia_between(519					l.syntax().clone(),520					l.semi_token().map(Into::into).as_ref(),521					l.expr()522						.map(|e| e.syntax().clone())523						.map(Into::into)524						.as_ref(),525				);526527				if expr_comments.should_start_with_newline {528					p!(pi: nl);529				}530				p!(pi: items(format_comments(&expr_comments.trivia, CommentLocation::AboveItem)));531				p!(pi: {l.expr()});532				pi533			}534			Expr::ExprIfThenElse(ite) => {535				let mut pi =536					p!(new: str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});537				if ite.else_kw_token().is_some() || ite.else_().is_some() {538					p!(pi: str(" else ") {ite.else_().map(|t| t.expr())})539				}540				pi541			}542			Expr::ExprFunction(f) => p!(new: str("function") {f.params_desc()} str(" ") {f.expr()}),543			Expr::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),544			Expr::ExprError(e) => p!(new: str("error ") {e.expr()}),545			Expr::ExprLiteral(l) => {546				p!(new: {l.literal()})547			}548		}549	}550}551552impl Printable for SourceFile {553	fn print(&self) -> PrintItems {554		let mut pi = p!(new:);555		let before = trivia_before(556			self.syntax().clone(),557			self.expr()558				.map(|e| e.syntax().clone())559				.map(Into::into)560				.as_ref(),561		);562		let after = trivia_after(563			self.syntax().clone(),564			self.expr()565				.map(|e| e.syntax().clone())566				.map(Into::into)567				.as_ref(),568		);569		p!(pi: items(format_comments(&before, CommentLocation::AboveItem)));570		p!(pi: {self.expr()} nl);571		p!(pi: items(format_comments(&after, CommentLocation::EndOfItems)));572		pi573	}574}575576fn main() {577	let (parsed, _errors) = jrsonnet_rowan_parser::parse(578		r#"579580581		# Edit me!582		local b = import "b.libsonnet";  # comment583		local a = import "a.libsonnet";584585			 local f(x,y)=x+y;586587		local {a: [b, ..., c], d, ...e} = null;588589		local ass = assert false : false; false;590591		local fn = function(a, b, c = 3) 4;592593		local comp = [a for b in c if d == e];594		local ocomp = {[k]: 1 for k in v};595596		local ? = skip;597598		local ie = a[expr];599600		local unary = !a;601602		local603			//   I am comment604			singleLocalWithItemComment = 1,605		;606607		// Comment between local and expression608609		local610			a = 1, //   Inline611			// Comment above b612			b = 4,613614			// c needs some space615			c = 5,616617			// Comment after everything618		;619620621		local Template = {z: "foo"};622623		{624						local625626					h = 3,627					assert self.a == 1628629					: "error",630		"f": ((((((3)))))) ,631		"g g":632		f(4,2),633		arr: [[634		  1, 2,635		  ],636		  3,637		  {638			  b: {639				  c: {640					  k: [16]641				  }642			  }643		  }644		  ],645		  m: a[1::],646		  m: b[::],647648		  comments: {649			_: '',650			//     Plain comment651			a: '',652653			#    Plain comment with empty line before654			b: '',655			/*Single-line multiline comment656657			*/658			c: '',659660			/**Single-line multiline doc comment661662			*/663			c: '',664665			/**multiline doc comment666			s667			*/668			c: '',669670			/*671672	Multi-line673674	comment675			*/676			d: '',677678			e: '', // Inline comment679680			k: '',681682			// Text after everything683		  },684		  comments2: {685			k: '',686			// Text after everything, but no newline above687		  },688		  k: if a         == b    then689690691		  2692693		  else Template {},694695		  compspecs: {696			obj_with_no_item: {for i in [1, 2, 3]},697			obj_with_2_items: {a:1, b:2, for i in [1,2,3]},698		  }699700		} + Template701702703		// Comment after everything704"#,705	);706707	// dbg!(errors);708	dbg!(&parsed);709710	let o = dprint_core::formatting::format(711		|| parsed.print(),712		PrintOptions {713			indent_width: 2,714			max_width: 100,715			use_tabs: false,716			new_line_text: "\n",717		},718	);719	println!("{}", o);720}
after · cmds/jrsonnet-fmt/src/main.rs
1use std::any::type_name;23use children::{children_between, trivia_before};4use dprint_core::formatting::{PrintItems, PrintOptions};5use jrsonnet_rowan_parser::{6	nodes::{7		ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,8		DestructRest, Expr, FieldName, ForSpec, IfSpec, ImportKind, LhsExpr, Literal, Member, Name,9		Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Text, UnaryOperator,10		Visibility, VisibilityKind,11	},12	rowan::NodeOrToken,13	AstNode, AstToken, SyntaxToken,14};1516use crate::{17	children::{trivia_after, trivia_between},18	comments::{format_comments, CommentLocation},19};2021mod children;22mod comments;23#[cfg(test)]24mod tests;2526pub trait Printable {27	fn print(&self) -> PrintItems;28}2930macro_rules! pi {31	(@i; $($t:tt)*) => {{32		#[allow(unused_mut)]33		let mut o = dprint_core::formatting::PrintItems::new();34		pi!(@s; o: $($t)*);35		o36	}};37	(@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{38		$o.push_str($e);39		pi!(@s; $o: $($t)*);40	}};41	(@s; $o:ident: string($e:expr $(,)?) $($t:tt)*) => {{42		$o.push_string($e);43		pi!(@s; $o: $($t)*);44	}};45	(@s; $o:ident: nl $($t:tt)*) => {{46		$o.push_signal(dprint_core::formatting::Signal::NewLine);47		pi!(@s; $o: $($t)*);48	}};49	(@s; $o:ident: tab $($t:tt)*) => {{50		$o.push_signal(dprint_core::formatting::Signal::Tab);51		pi!(@s; $o: $($t)*);52	}};53	(@s; $o:ident: >i $($t:tt)*) => {{54		$o.push_signal(dprint_core::formatting::Signal::StartIndent);55		pi!(@s; $o: $($t)*);56	}};57	(@s; $o:ident: <i $($t:tt)*) => {{58		$o.push_signal(dprint_core::formatting::Signal::FinishIndent);59		pi!(@s; $o: $($t)*);60	}};61	(@s; $o:ident: {$expr:expr} $($t:tt)*) => {{62		$o.extend($expr.print());63		pi!(@s; $o: $($t)*);64	}};65	(@s; $o:ident: items($expr:expr) $($t:tt)*) => {{66		$o.extend($expr);67		pi!(@s; $o: $($t)*);68	}};69	(@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{70		if $e {71			pi!(@s; $o: $($then)*);72		}73		pi!(@s; $o: $($t)*);74	}};75	(@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{76		if $e {77			pi!(@s; $o: $($then)*);78		} else {79			pi!(@s; $o: $($else)*);80		}81		pi!(@s; $o: $($t)*);82	}};83	(@s; $i:ident:) => {}84}85macro_rules! p {86	(new: $($t:tt)*) => {87		pi!(@i; $($t)*)88	};89	($o:ident: $($t:tt)*) => {90		pi!(@s; $o: $($t)*)91	};92}93pub(crate) use p;94pub(crate) use pi;9596impl<P> Printable for Option<P>97where98	P: Printable,99{100	fn print(&self) -> PrintItems {101		if let Some(v) = self {102			v.print()103		} else {104			p!(new: string(105				format!(106					"/*missing {}*/",107					type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")108				),109			))110		}111	}112}113114impl Printable for SyntaxToken {115	fn print(&self) -> PrintItems {116		p!(new: string(self.to_string()))117	}118}119120impl Printable for Text {121	fn print(&self) -> PrintItems {122		p!(new: string(format!("{}", self)))123	}124}125impl Printable for Number {126	fn print(&self) -> PrintItems {127		p!(new: string(format!("{}", self)))128	}129}130131impl Printable for Name {132	fn print(&self) -> PrintItems {133		p!(new: {self.ident_lit()})134	}135}136137impl Printable for DestructRest {138	fn print(&self) -> PrintItems {139		let mut pi = p!(new: str("..."));140		if let Some(name) = self.into() {141			p!(pi: {name});142		}143		pi144	}145}146147impl Printable for Destruct {148	fn print(&self) -> PrintItems {149		let mut pi = p!(new:);150		match self {151			Destruct::DestructFull(f) => {152				p!(pi: {f.name()})153			}154			Destruct::DestructSkip(_) => p!(pi: str("?")),155			Destruct::DestructArray(a) => {156				p!(pi: str("[") >i nl);157				for el in a.destruct_array_parts() {158					match el {159						DestructArrayPart::DestructArrayElement(e) => {160							p!(pi: {e.destruct()} str(",") nl)161						}162						DestructArrayPart::DestructRest(d) => {163							p!(pi: {d} str(",") nl)164						}165					}166				}167				p!(pi: <i str("]"));168			}169			Destruct::DestructObject(o) => {170				p!(pi: str("{") >i nl);171				for item in o.destruct_object_fields() {172					p!(pi: {item.field()});173					if let Some(des) = item.destruct() {174						p!(pi: str(": ") {des})175					}176					if let Some(def) = item.expr() {177						p!(pi: str(" = ") {def});178					}179					p!(pi: str(",") nl);180				}181				if let Some(rest) = o.destruct_rest() {182					p!(pi: {rest} nl)183				}184				p!(pi: <i str("}"));185			}186		}187		pi188	}189}190191impl Printable for FieldName {192	fn print(&self) -> PrintItems {193		match self {194			FieldName::FieldNameFixed(f) => {195				if let Some(id) = f.id() {196					p!(new: {id})197				} else if let Some(str) = f.text() {198					p!(new: {str})199				} else {200					p!(new: str("/*missing FieldName*/"))201				}202			}203			FieldName::FieldNameDynamic(d) => {204				p!(new: str("[") {d.expr()} str("]"))205			}206		}207	}208}209210impl Printable for Visibility {211	fn print(&self) -> PrintItems {212		p!(new: string(self.to_string()))213	}214}215216impl Printable for ObjLocal {217	fn print(&self) -> PrintItems {218		p!(new: str("local ") {self.bind()})219	}220}221222impl Printable for Assertion {223	fn print(&self) -> PrintItems {224		let mut pi = p!(new: str("assert ") {self.condition()});225		if self.colon_token().is_some() || self.message().is_some() {226			p!(pi: str(": ") {self.message()})227		}228		pi229	}230}231232impl Printable for ParamsDesc {233	fn print(&self) -> PrintItems {234		let mut pi = p!(new: str("(") >i nl);235		for param in self.params() {236			p!(pi: {param.destruct()});237			if param.assign_token().is_some() || param.expr().is_some() {238				p!(pi: str(" = ") {param.expr()})239			}240			p!(pi: str(",") nl)241		}242		p!(pi: <i str(")"));243		pi244	}245}246impl Printable for ArgsDesc {247	fn print(&self) -> PrintItems {248		let mut pi = p!(new: str("(") >i nl);249		for arg in self.args() {250			if arg.name().is_some() || arg.assign_token().is_some() {251				p!(pi: {arg.name()} str(" = "));252			}253			p!(pi: {arg.expr()} str(",") nl)254		}255		p!(pi: <i str(")"));256		pi257	}258}259impl Printable for SliceDesc {260	fn print(&self) -> PrintItems {261		let mut pi = p!(new: str("["));262		if self.from().is_some() {263			p!(pi: {self.from()});264		}265		p!(pi: str(":"));266		if self.end().is_some() {267			p!(pi: {self.end().map(|e|e.expr())})268		}269		// Keep only one : in case if we don't need step270		if self.step().is_some() {271			p!(pi: str(":") {self.step().map(|e|e.expr())});272		}273		p!(pi: str("]"));274		pi275	}276}277278impl Printable for Member {279	fn print(&self) -> PrintItems {280		match self {281			Member::MemberBindStmt(b) => {282				p!(new: {b.obj_local()})283			}284			Member::MemberAssertStmt(ass) => {285				p!(new: {ass.assertion()})286			}287			Member::MemberFieldNormal(n) => {288				p!(new: {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()})289			}290			Member::MemberFieldMethod(_) => todo!(),291		}292	}293}294295impl Printable for ObjBody {296	fn print(&self) -> PrintItems {297		match self {298			ObjBody::ObjBodyComp(l) => {299				let (children, end_comments) = children_between::<Member>(300					l.syntax().clone(),301					l.l_brace_token().map(Into::into).as_ref(),302					Some(303						&(l.comp_specs()304							.next()305							.expect("at least one spec is defined")306							.syntax()307							.clone())308						.into(),309					),310				);311				let mut pi = p!(new: str("{") >i nl);312				for mem in children.into_iter() {313					if mem.should_start_with_newline {314						p!(pi: nl);315					}316					p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));317					p!(pi: {mem.value} str(","));318					p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));319					p!(pi: nl)320				}321322				if end_comments.should_start_with_newline {323					p!(pi: nl);324				}325				p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));326327				let (compspecs, end_comments) = children_between::<CompSpec>(328					l.syntax().clone(),329					l.member_comps()330						.last()331						.map(|m| m.syntax().clone())332						.map(Into::into)333						.or_else(|| l.l_brace_token().map(Into::into))334						.as_ref(),335					l.r_brace_token().map(Into::into).as_ref(),336				);337				for mem in compspecs.into_iter() {338					if mem.should_start_with_newline {339						p!(pi: nl);340					}341					p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));342					p!(pi: {mem.value});343					p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));344				}345				if end_comments.should_start_with_newline {346					p!(pi: nl);347				}348				p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));349350				p!(pi: <i str("}"));351				pi352			}353			ObjBody::ObjBodyMemberList(l) => {354				let (children, end_comments) = children_between::<Member>(355					l.syntax().clone(),356					l.l_brace_token().map(Into::into).as_ref(),357					l.r_brace_token().map(Into::into).as_ref(),358				);359				if children.is_empty() && end_comments.is_empty() {360					return p!(new: str("{ }"));361				}362				let mut pi = p!(new: str("{") >i nl);363				for mem in children.into_iter() {364					if mem.should_start_with_newline {365						p!(pi: nl);366					}367					p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));368					p!(pi: {mem.value} str(","));369					p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));370					p!(pi: nl)371				}372373				if end_comments.should_start_with_newline {374					p!(pi: nl);375				}376				p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));377				p!(pi: <i str("}"));378				pi379			}380		}381	}382}383impl Printable for UnaryOperator {384	fn print(&self) -> PrintItems {385		p!(new: string(self.text().to_string()))386	}387}388impl Printable for BinaryOperator {389	fn print(&self) -> PrintItems {390		p!(new: string(self.text().to_string()))391	}392}393impl Printable for Bind {394	fn print(&self) -> PrintItems {395		match self {396			Bind::BindDestruct(d) => {397				p!(new: {d.into()} str(" = ") {d.value()})398			}399			Bind::BindFunction(f) => {400				p!(new: {f.name()} {f.params()} str(" = ") {f.value()})401			}402		}403	}404}405impl Printable for Literal {406	fn print(&self) -> PrintItems {407		p!(new: string(self.syntax().to_string()))408	}409}410impl Printable for ImportKind {411	fn print(&self) -> PrintItems {412		p!(new: string(self.syntax().to_string()))413	}414}415impl Printable for LhsExpr {416	fn print(&self) -> PrintItems {417		p!(new: {self.expr()})418	}419}420impl Printable for ForSpec {421	fn print(&self) -> PrintItems {422		p!(new: str("for ") {self.bind()} str(" in ") {self.expr()})423	}424}425impl Printable for IfSpec {426	fn print(&self) -> PrintItems {427		p!(new: str("if ") {self.expr()})428	}429}430impl Printable for CompSpec {431	fn print(&self) -> PrintItems {432		match self {433			CompSpec::ForSpec(f) => f.print(),434			CompSpec::IfSpec(i) => i.print(),435		}436	}437}438impl Printable for Expr {439	fn print(&self) -> PrintItems {440		match self {441			Expr::ExprBinary(b) => {442				p!(new: {b.lhs()} str(" ") {b.binary_operator()} str(" ") {b.rhs()})443			}444			Expr::ExprUnary(u) => p!(new: {u.unary_operator()} {u.rhs()}),445			Expr::ExprSlice(s) => {446				p!(new: {s.expr()} {s.slice_desc()})447			}448			Expr::ExprIndex(i) => {449				p!(new: {i.expr()} str(".") {i.index()})450			}451			Expr::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),452			Expr::ExprApply(a) => {453				let mut pi = p!(new: {a.expr()} {a.args_desc()});454				if a.tailstrict_kw_token().is_some() {455					p!(pi: str(" tailstrict"));456				}457				pi458			}459			Expr::ExprObjExtend(ex) => {460				p!(new: {ex.lhs_expr()} str(" ") {ex.expr()})461			}462			Expr::ExprParened(p) => {463				p!(new: str("(") {p.expr()} str(")"))464			}465			Expr::ExprString(s) => p!(new: {s.text()}),466			Expr::ExprNumber(n) => p!(new: {n.number()}),467			Expr::ExprArray(a) => {468				let mut pi = p!(new: str("[") >i nl);469				for el in a.exprs() {470					p!(pi: {el} str(",") nl);471				}472				p!(pi: <i str("]"));473				pi474			}475			Expr::ExprObject(o) => {476				p!(new: {o.obj_body()})477			}478			Expr::ExprArrayComp(arr) => {479				let mut pi = p!(new: str("[") {arr.expr()});480				for spec in arr.comp_specs() {481					p!(pi: str(" ") {spec});482				}483				p!(pi: str("]"));484				pi485			}486			Expr::ExprImport(v) => {487				p!(new: {v.import_kind()} str(" ") {v.text()})488			}489			Expr::ExprVar(n) => p!(new: {n.name()}),490			Expr::ExprLocal(l) => {491				let mut pi = p!(new:);492				let (binds, end_comments) = children_between::<Bind>(493					l.syntax().clone(),494					l.local_kw_token().map(Into::into).as_ref(),495					l.semi_token().map(Into::into).as_ref(),496				);497				if binds.len() == 1 {498					let bind = &binds[0];499					p!(pi: items(format_comments(&bind.before_trivia, CommentLocation::AboveItem)));500					p!(pi: str("local ") {bind.value});501				// TODO: keep end_comments, child.inline_trivia somehow, force multiple locals formatting in case of presence?502				} else {503					p!(pi: str("local") >i nl);504					for bind in binds {505						if bind.should_start_with_newline {506							p!(pi: nl);507						}508						p!(pi: items(format_comments(&bind.before_trivia, CommentLocation::AboveItem)));509						p!(pi: {bind.value} str(","));510						p!(pi: items(format_comments(&bind.inline_trivia, CommentLocation::ItemInline)) nl);511					}512					if end_comments.should_start_with_newline {513						p!(pi: nl)514					}515					p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));516					p!(pi: <i);517				}518				p!(pi: str(";") nl);519520				let expr_comments = trivia_between(521					l.syntax().clone(),522					l.semi_token().map(Into::into).as_ref(),523					l.expr()524						.map(|e| e.syntax().clone())525						.map(Into::into)526						.as_ref(),527				);528529				if expr_comments.should_start_with_newline {530					p!(pi: nl);531				}532				p!(pi: items(format_comments(&expr_comments.trivia, CommentLocation::AboveItem)));533				p!(pi: {l.expr()});534				pi535			}536			Expr::ExprIfThenElse(ite) => {537				let mut pi =538					p!(new: str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});539				if ite.else_kw_token().is_some() || ite.else_().is_some() {540					p!(pi: str(" else ") {ite.else_().map(|t| t.expr())})541				}542				pi543			}544			Expr::ExprFunction(f) => p!(new: str("function") {f.params_desc()} str(" ") {f.expr()}),545			Expr::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),546			Expr::ExprError(e) => p!(new: str("error ") {e.expr()}),547			Expr::ExprLiteral(l) => {548				p!(new: {l.literal()})549			}550		}551	}552}553554impl Printable for SourceFile {555	fn print(&self) -> PrintItems {556		let mut pi = p!(new:);557		let before = trivia_before(558			self.syntax().clone(),559			self.expr()560				.map(|e| e.syntax().clone())561				.map(Into::into)562				.as_ref(),563		);564		let after = trivia_after(565			self.syntax().clone(),566			self.expr()567				.map(|e| e.syntax().clone())568				.map(Into::into)569				.as_ref(),570		);571		p!(pi: items(format_comments(&before, CommentLocation::AboveItem)));572		p!(pi: {self.expr()} nl);573		p!(pi: items(format_comments(&after, CommentLocation::EndOfItems)));574		pi575	}576}577578fn format(input: &str) -> String {579	let (parsed, errors) = jrsonnet_rowan_parser::parse(input);580	if !errors.is_empty() {581		let mut builder = ass_stroke::SnippetBuilder::new(input);582		for error in errors {583			builder584				.error(ass_stroke::Text::single(585					format!("{:?}", error.error).chars(),586					Default::default(),587				))588				.range(589					error.range.start().into()590						..=(usize::from(error.range.end()) - 1).max(error.range.start().into()),591				)592				.build();593		}594		let snippet = builder.build();595		let ansi = ass_stroke::source_to_ansi(&snippet);596		println!("{ansi}");597	}598	dprint_core::formatting::format(599		|| parsed.print(),600		PrintOptions {601			indent_width: 2,602			max_width: 100,603			use_tabs: false,604			new_line_text: "\n",605		},606	)607}608fn main() {609	let input = r#"610611612		# Edit me!613		local b = import "b.libsonnet";  # comment614		local a = import "a.libsonnet";615616		local f(x,y)=x+y;617618		local {a: [b, ..., c], d, ...e} = null;619620		local ass = assert false : false; false;621622		local fn = function(a, b, c = 3) 4;623624		local comp = [a for b in c if d == e];625		local ocomp = {[k]: 1 for k in v};626627		local ? = skip;628629		local ie = a[expr];630631		local unary = !a;632633		local634			//   I am comment635			singleLocalWithItemComment = 1,636		;637638		// Comment between local and expression639640		local641			a = 1, //   Inline642			// Comment above b643			b = 4,644645			// c needs some space646			c = 5,647648			// Comment after everything649		;650651652		local Template = {z: "foo"};653654		{655						local656657					h = 3,658					assert self.a == 1659660					: "error",661		"f": ((((((3)))))) ,662		"g g":663		f(4,2),664		arr: [[665		  1, 2,666		  ],667		  3,668		  {669			  b: {670				  c: {671					  k: [16]672				  }673			  }674		  }675		  ],676		  m: a[1::],677		  m: b[::],678679		  comments: {680			_: '',681			//     Plain comment682			a: '',683684			#    Plain comment with empty line before685			b: '',686			/*Single-line multiline comment687688			*/689			c: '',690691			/**Single-line multiline doc comment692693			*/694			c: '',695696			/**multiline doc comment697			s698			*/699			c: '',700701			/*702703	Multi-line704705	comment706			*/707			d: '',708709			e: '', // Inline comment710711			k: '',712713			// Text after everything714		  },715		  comments2: {716			k: '',717			// Text after everything, but no newline above718		  },719		  k: if a         == b    then720721722		  2723724		  else Template {},725726		  compspecs: {727			obj_with_no_item: {a:1, for i in [1, 2, 3]},728			obj_with_2_items: {a:1, /*b:2,*/ for i in [1,2,3]},729		  }730731		} + Template732"#;733734	let mut iteration = 0;735	let mut a = input.to_string();736	let mut b;737	// https://github.com/dprint/dprint/pull/423738	loop {739		b = format(&a).trim().to_owned();740		if a == b {741			break;742		}743		println!("{b}");744		a = b;745		iteration += 1;746		if iteration > 5 {747			panic!("formatting not converged");748			break;749		}750	}751	println!("{a}");752}
modifiedcmds/jrsonnet/Cargo.tomldiffbeforeafterboth
--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -12,10 +12,7 @@
 # Use mimalloc as allocator
 mimalloc = ["mimallocator"]
 # Experimental feature, which allows to preserve order of object fields
-exp-preserve-order = [
-    "jrsonnet-evaluator/exp-preserve-order",
-    "jrsonnet-cli/exp-preserve-order",
-]
+exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order", "jrsonnet-cli/exp-preserve-order"]
 # Destructuring of locals
 exp-destruct = ["jrsonnet-evaluator/exp-destruct"]
 # Iteration over objects yields [key, value] elements
@@ -44,3 +41,4 @@
 clap_complete = { version = "4.1" }
 serde_json = "1.0.104"
 serde = { workspace = true, features = ["derive"] }
+ass-stroke = { git = "https://github.com/CertainLach/ass-stroke", version = "0.1.0" }