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}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" }