difftreelog
feat(fmt) try to use ass-stroke
in: master
4 files changed
cmds/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" }
cmds/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()
+ }
+}
cmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth1use 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}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}cmds/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" }