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

difftreelog

refactor split pretty-printer into a library crate

tvsrnnvwYaroslav Bolyukin2026-02-08parent: #6e67554.patch.diff
in: master

14 files changed

modifiedCargo.lockdiffbeforeafterboth
631version = "0.5.0-pre97"631version = "0.5.0-pre97"
632dependencies = [632dependencies = [
633 "clap",633 "clap",
634 "dprint-core",
635 "hi-doc",634 "hi-doc",
636 "indoc",635 "indoc",
637 "insta",
638 "jrsonnet-rowan-parser",636 "jrsonnet-formatter",
639 "tempfile",637 "tempfile",
640 "thiserror",638 "thiserror",
641]639]
640
641[[package]]
642name = "jrsonnet-formatter"
643version = "0.5.0-pre97"
644dependencies = [
645 "dprint-core",
646 "hi-doc",
647 "jrsonnet-rowan-parser",
648]
642649
643[[package]]650[[package]]
644name = "jrsonnet-gcmodule"651name = "jrsonnet-gcmodule"
modifiedCargo.tomldiffbeforeafterboth
19jrsonnet-stdlib = { path = "./crates/jrsonnet-stdlib", version = "0.5.0-pre97" }19jrsonnet-stdlib = { path = "./crates/jrsonnet-stdlib", version = "0.5.0-pre97" }
20jrsonnet-cli = { path = "./crates/jrsonnet-cli", version = "0.5.0-pre97" }20jrsonnet-cli = { path = "./crates/jrsonnet-cli", version = "0.5.0-pre97" }
21jrsonnet-types = { path = "./crates/jrsonnet-types", version = "0.5.0-pre97" }21jrsonnet-types = { path = "./crates/jrsonnet-types", version = "0.5.0-pre97" }
22jrsonnet-formatter = { path = "./crates/jrsonnet-formatter", version = "0.5.0-pre97" }
22jrsonnet-gcmodule = { version = "0.4.1" }23jrsonnet-gcmodule = { version = "0.4.1" }
23# Diagnostics.24# Diagnostics.
24# hi-doc is my library, which handles text formatting very well, but isn't polished enough yet25# hi-doc is my library, which handles text formatting very well, but isn't polished enough yet
modifiedcmds/jrsonnet-fmt/Cargo.tomldiffbeforeafterboth
10workspace = true10workspace = true
1111
12[dependencies]12[dependencies]
13dprint-core.workspace = true
14jrsonnet-rowan-parser.workspace = true
15insta.workspace = true
16indoc.workspace = true13indoc.workspace = true
17hi-doc.workspace = true14hi-doc.workspace = true
18clap = { workspace = true, features = ["derive"] }15clap = { workspace = true, features = ["derive"] }
19tempfile.workspace = true16tempfile.workspace = true
20thiserror.workspace = true17thiserror.workspace = true
18jrsonnet-formatter.workspace = true
2119
deletedcmds/jrsonnet-fmt/src/children.rsdiffbeforeafterboth

no changes

deletedcmds/jrsonnet-fmt/src/comments.rsdiffbeforeafterboth

no changes

modifiedcmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth
1use std::{1use std::{fs, io};
2 any::type_name,2
3 fs,
4 io::{self, Write},
5 path::PathBuf,
6 process,
7 rc::Rc,
8};
9
10use children::{children_between, trivia_before};
11use clap::Parser;3use clap::Parser;
12use dprint_core::formatting::{4use jrsonnet_formatter::{format, FormatOptions};
13 condition_helpers::is_multiple_lines, condition_resolvers::true_resolver,
14 ConditionResolverContext, LineNumber, PrintItems, PrintOptions,
15};
16use hi_doc::Formatting;
17use jrsonnet_rowan_parser::{
18 nodes::{
19 Arg, ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,
20 DestructRest, Expr, ExprBase, FieldName, ForSpec, IfSpec, ImportKind, Literal, Member,
21 Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Stmt, Suffix, Text,
22 UnaryOperator, Visibility,
23 },
24 AstNode, AstToken as _, SyntaxToken,
25};
26
27use crate::{
28 children::trivia_after,
29 comments::{format_comments, CommentLocation},
30};
31
32mod children;
33mod comments;
34#[cfg(test)]
35mod tests;
36
37pub trait Printable {
38 fn print(&self, out: &mut PrintItems);
39}
40
41macro_rules! pi {
42 (@i; $($t:tt)*) => {{
43 #[allow(unused_mut)]
44 let mut o = dprint_core::formatting::PrintItems::new();
45 pi!(@s; o: $($t)*);
46 o
47 }};
48 (@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{
49 $o.push_string($e.to_owned());
50 pi!(@s; $o: $($t)*);
51 }};
52 (@s; $o:ident: string($e:expr $(,)?) $($t:tt)*) => {{
53 $o.push_string($e);
54 pi!(@s; $o: $($t)*);
55 }};
56 (@s; $o:ident: nl $($t:tt)*) => {{
57 $o.push_signal(dprint_core::formatting::Signal::NewLine);
58 pi!(@s; $o: $($t)*);
59 }};
60 (@s; $o:ident: tab $($t:tt)*) => {{
61 $o.push_signal(dprint_core::formatting::Signal::Tab);
62 pi!(@s; $o: $($t)*);
63 }};
64 (@s; $o:ident: >i $($t:tt)*) => {{
65 $o.push_signal(dprint_core::formatting::Signal::StartIndent);
66 pi!(@s; $o: $($t)*);
67 }};
68 (@s; $o:ident: <i $($t:tt)*) => {{
69 $o.push_signal(dprint_core::formatting::Signal::FinishIndent);
70 pi!(@s; $o: $($t)*);
71 }};
72 (@s; $o:ident: info($v:expr) $($t:tt)*) => {{
73 $o.push_info($v);
74 pi!(@s; $o: $($t)*);
75 }};
76 (@s; $o:ident: if($s:literal, $cond:expr, $($i:tt)*) $($t:tt)*) => {{
77 $o.push_condition(dprint_core::formatting::conditions::if_true(
78 $s,
79 $cond.clone(),
80 {
81 let mut o = PrintItems::new();
82 p!(o, $($i)*);
83 o
84 },
85 ));
86 pi!(@s; $o: $($t)*);
87 }};
88 (@s; $o:ident: if_else($s:literal, $cond:expr, $($i:tt)*)($($e:tt)+) $($t:tt)*) => {{
89 $o.push_condition(dprint_core::formatting::conditions::if_true_or(
90 $s,
91 $cond.clone(),
92 {
93 let mut o = PrintItems::new();
94 p!(o, $($i)*);
95 o
96 },
97 {
98 let mut o = PrintItems::new();
99 p!(o, $($e)*);
100 o
101 },
102 ));
103 pi!(@s; $o: $($t)*);
104 }};
105 (@s; $o:ident: if_not($s:literal, $cond:expr, $($e:tt)*) $($t:tt)*) => {{
106 $o.push_condition(dprint_core::formatting::conditions::if_true_or(
107 $s,
108 $cond.clone(),
109 {
110 let o = PrintItems::new();
111 o
112 },
113 {
114 let mut o = PrintItems::new();
115 p!(o, $($e)*);
116 o
117 },
118 ));
119 pi!(@s; $o: $($t)*);
120 }};
121 (@s; $o:ident: {$expr:expr} $($t:tt)*) => {{
122 $expr.print($o);
123 pi!(@s; $o: $($t)*);
124 }};
125 (@s; $o:ident: items($expr:expr) $($t:tt)*) => {{
126 $o.extend($expr);
127 pi!(@s; $o: $($t)*);
128 }};
129 (@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{
130 if $e {
131 pi!(@s; $o: $($then)*);
132 }
133 pi!(@s; $o: $($t)*);
134 }};
135 (@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{
136 if $e {
137 pi!(@s; $o: $($then)*);
138 } else {
139 pi!(@s; $o: $($else)*);
140 }
141 pi!(@s; $o: $($t)*);
142 }};
143 (@s; $i:ident:) => {}
144}
145macro_rules! p {
146 ($o:ident, $($t:tt)*) => {
147 pi!(@s; $o: $($t)*)
148 };
149}
150pub(crate) use p;
151pub(crate) use pi;
152
153impl<P> Printable for Option<P>
154where
155 P: Printable,
156{
157 fn print(&self, out: &mut PrintItems) {
158 if let Some(v) = self {
159 v.print(out);
160 } else {
161 p!(
162 out,
163 string(format!(
164 "/*missing {}*/",
165 type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")
166 ),)
167 );
168 }
169 }
170}
171
172impl Printable for SyntaxToken {
173 fn print(&self, out: &mut PrintItems) {
174 p!(out, string(self.to_string()));
175 }
176}
177
178impl Printable for Text {
179 fn print(&self, out: &mut PrintItems) {
180 p!(out, string(format!("{}", self)));
181 }
182}
183impl Printable for Number {
184 fn print(&self, out: &mut PrintItems) {
185 p!(out, string(format!("{}", self)));
186 }
187}
188
189impl Printable for Name {
190 fn print(&self, out: &mut PrintItems) {
191 p!(out, { self.ident_lit() });
192 }
193}
194
195impl Printable for DestructRest {
196 fn print(&self, out: &mut PrintItems) {
197 p!(out, str("..."));
198 if let Some(name) = self.into() {
199 p!(out, { name });
200 }
201 }
202}
203
204impl Printable for Destruct {
205 fn print(&self, out: &mut PrintItems) {
206 match self {
207 Self::DestructFull(f) => {
208 p!(out, { f.name() });
209 }
210 Self::DestructSkip(_) => p!(out, str("?")),
211 Self::DestructArray(a) => {
212 p!(out, str("[") >i nl);
213 for el in a.destruct_array_parts() {
214 match el {
215 DestructArrayPart::DestructArrayElement(e) => {
216 p!(out, {e.destruct()} str(",") nl);
217 }
218 DestructArrayPart::DestructRest(d) => {
219 p!(out, {d} str(",") nl);
220 }
221 }
222 }
223 p!(out, <i str("]"));
224 }
225 Self::DestructObject(o) => {
226 p!(out, str("{") >i nl);
227 for item in o.destruct_object_fields() {
228 p!(out, { item.field() });
229 if let Some(des) = item.destruct() {
230 p!(out, str(": ") {des});
231 }
232 if let Some(def) = item.expr() {
233 p!(out, str(" = ") {def});
234 }
235 p!(out, str(",") nl);
236 }
237 if let Some(rest) = o.destruct_rest() {
238 p!(out, {rest} nl);
239 }
240 p!(out, <i str("}"));
241 }
242 }
243 }
244}
245
246impl Printable for FieldName {
247 fn print(&self, out: &mut PrintItems) {
248 match self {
249 Self::FieldNameFixed(f) => {
250 if let Some(id) = f.id() {
251 p!(out, { id });
252 } else if let Some(str) = f.text() {
253 p!(out, { str });
254 } else {
255 p!(out, str("/*missing FieldName*/"));
256 }
257 }
258 Self::FieldNameDynamic(d) => {
259 p!(out, str("[") {d.expr()} str("]"));
260 }
261 }
262 }
263}
264
265impl Printable for Visibility {
266 fn print(&self, out: &mut PrintItems) {
267 p!(out, string(self.to_string()));
268 }
269}
270
271impl Printable for ObjLocal {
272 fn print(&self, out: &mut PrintItems) {
273 p!(out, str("local ") {self.bind()});
274 }
275}
276
277impl Printable for Assertion {
278 fn print(&self, out: &mut PrintItems) {
279 p!(out, str("assert ") {self.condition()});
280 if self.colon_token().is_some() || self.message().is_some() {
281 p!(out, str(": ") {self.message()});
282 }
283 }
284}
285
286impl Printable for ParamsDesc {
287 fn print(&self, out: &mut PrintItems) {
288 p!(out, str("(") >i nl);
289 for param in self.params() {
290 p!(out, { param.destruct() });
291 if param.assign_token().is_some() || param.expr().is_some() {
292 p!(out, str(" = ") {param.expr()});
293 }
294 p!(out, str(",") nl);
295 }
296 p!(out, <i str(")"));
297 }
298}
299impl Printable for ArgsDesc {
300 fn print(&self, out: &mut PrintItems) {
301 let start = LineNumber::new("start");
302 let end = LineNumber::new("end");
303 let multi_line = Rc::new(move |condition_context: &mut ConditionResolverContext| {
304 is_multiple_lines(condition_context, start, end).map(|v| !v)
305 });
306 p!(out, str("(") info(start) if("start args", multi_line, >i nl));
307 let (children, end_comments) = children_between::<Arg>(
308 self.syntax().clone(),
309 self.l_paren_token().map(Into::into).as_ref(),
310 self.r_paren_token().map(Into::into).as_ref(),
311 None,
312 );
313 let mut args = children.into_iter().peekable();
314 while let Some(ele) = args.next() {
315 if ele.should_start_with_newline {
316 p!(out, nl);
317 }
318 format_comments(&ele.before_trivia, CommentLocation::AboveItem, out);
319 let arg = ele.value;
320 if arg.name().is_some() || arg.assign_token().is_some() {
321 p!(out, {arg.name()} str(" = "));
322 }
323 let comma_between = if args.peek().is_some() {
324 true_resolver()
325 } else {
326 multi_line.clone()
327 };
328 p!(out, {arg.expr()} if("arg comma", comma_between, str(",") if_not("between args", multi_line, str(" "))));
329 format_comments(&ele.inline_trivia, CommentLocation::ItemInline, out);
330 p!(out, if("between args", multi_line, nl));
331 }
332 if end_comments.should_start_with_newline {
333 p!(out, nl);
334 }
335 format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);
336 p!(out, if("end args", multi_line, <i info(end)) str(")"));
337 }
338}
339impl Printable for SliceDesc {
340 fn print(&self, out: &mut PrintItems) {
341 p!(out, str("["));
342 if self.from().is_some() {
343 p!(out, { self.from() });
344 }
345 p!(out, str(":"));
346 if self.end().is_some() {
347 p!(out, { self.end().map(|e| e.expr()) });
348 }
349 // Keep only one : in case if we don't need step
350 if self.step().is_some() {
351 p!(out, str(":") {self.step().map(|e|e.expr())});
352 }
353 p!(out, str("]"));
354 }
355}
356
357impl Printable for Member {
358 fn print(&self, out: &mut PrintItems) {
359 match self {
360 Self::MemberBindStmt(b) => {
361 p!(out, { b.obj_local() });
362 }
363 Self::MemberAssertStmt(ass) => {
364 p!(out, { ass.assertion() });
365 }
366 Self::MemberFieldNormal(n) => {
367 p!(out, {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()});
368 }
369 Self::MemberFieldMethod(m) => {
370 p!(out, {m.field_name()} {m.params_desc()} {m.visibility()} str(" ") {m.expr()});
371 }
372 }
373 }
374}
375
376impl Printable for ObjBody {
377 fn print(&self, out: &mut PrintItems) {
378 match self {
379 Self::ObjBodyComp(l) => {
380 let (children, mut end_comments) = children_between::<Member>(
381 l.syntax().clone(),
382 l.l_brace_token().map(Into::into).as_ref(),
383 Some(
384 &(l.comp_specs()
385 .next()
386 .expect("at least one spec is defined")
387 .syntax()
388 .clone())
389 .into(),
390 ),
391 None,
392 );
393 let trailing_for_comp = end_comments.extract_trailing();
394 p!(out, str("{") >i nl);
395 for mem in children {
396 if mem.should_start_with_newline {
397 p!(out, nl);
398 }
399 format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);
400 p!(out, {mem.value} str(","));
401 format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);
402 p!(out, nl);
403 }
404
405 if end_comments.should_start_with_newline {
406 p!(out, nl);
407 }
408 format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);
409
410 let (compspecs, end_comments) = children_between::<CompSpec>(
411 l.syntax().clone(),
412 l.member_comps()
413 .last()
414 .map(|m| m.syntax().clone())
415 .map(Into::into)
416 .or_else(|| l.l_brace_token().map(Into::into))
417 .as_ref(),
418 l.r_brace_token().map(Into::into).as_ref(),
419 Some(trailing_for_comp),
420 );
421 for mem in compspecs {
422 if mem.should_start_with_newline {
423 p!(out, nl);
424 }
425 format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);
426 p!(out, { mem.value });
427 format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);
428 }
429 if end_comments.should_start_with_newline {
430 p!(out, nl);
431 }
432 format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);
433
434 p!(out, nl <i str("}"));
435 }
436 Self::ObjBodyMemberList(l) => {
437 let (children, end_comments) = children_between::<Member>(
438 l.syntax().clone(),
439 l.l_brace_token().map(Into::into).as_ref(),
440 l.r_brace_token().map(Into::into).as_ref(),
441 None,
442 );
443 if children.is_empty() && end_comments.is_empty() {
444 p!(out, str("{ }"));
445 return;
446 }
447 p!(out, str("{") >i nl);
448 for (i, mem) in children.into_iter().enumerate() {
449 if mem.should_start_with_newline && i != 0 {
450 p!(out, nl);
451 }
452 format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);
453 p!(out, {mem.value} str(","));
454 format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);
455 p!(out, nl);
456 }
457
458 if end_comments.should_start_with_newline {
459 p!(out, nl);
460 }
461 format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);
462 p!(out, <i str("}"));
463 }
464 }
465 }
466}
467impl Printable for UnaryOperator {
468 fn print(&self, out: &mut PrintItems) {
469 p!(out, string(self.text().to_string()));
470 }
471}
472impl Printable for BinaryOperator {
473 fn print(&self, out: &mut PrintItems) {
474 p!(out, string(self.text().to_string()));
475 }
476}
477impl Printable for Bind {
478 fn print(&self, out: &mut PrintItems) {
479 match self {
480 Self::BindDestruct(d) => {
481 p!(out, {d.into()} str(" = ") {d.value()});
482 }
483 Self::BindFunction(f) => {
484 p!(out, {f.name()} {f.params()} str(" = ") {f.value()});
485 }
486 }
487 }
488}
489impl Printable for Literal {
490 fn print(&self, out: &mut PrintItems) {
491 p!(out, string(self.syntax().to_string()));
492 }
493}
494impl Printable for ImportKind {
495 fn print(&self, out: &mut PrintItems) {
496 p!(out, string(self.syntax().to_string()));
497 }
498}
499impl Printable for ForSpec {
500 fn print(&self, out: &mut PrintItems) {
501 p!(out, str("for ") {self.bind()} str(" in ") {self.expr()});
502 }
503}
504impl Printable for IfSpec {
505 fn print(&self, out: &mut PrintItems) {
506 p!(out, str("if ") {self.expr()});
507 }
508}
509impl Printable for CompSpec {
510 fn print(&self, out: &mut PrintItems) {
511 match self {
512 Self::ForSpec(f) => f.print(out),
513 Self::IfSpec(i) => i.print(out),
514 }
515 }
516}
517impl Printable for Expr {
518 fn print(&self, out: &mut PrintItems) {
519 let (stmts, _ending) = children_between::<Stmt>(
520 self.syntax().clone(),
521 None,
522 self.expr_base()
523 .as_ref()
524 .map(ExprBase::syntax)
525 .cloned()
526 .map(Into::into)
527 .as_ref(),
528 None,
529 );
530 for stmt in stmts {
531 p!(out, { stmt.value });
532 }
533 p!(out, { self.expr_base() });
534 let (suffixes, _ending) = children_between::<Suffix>(
535 self.syntax().clone(),
536 self.expr_base()
537 .as_ref()
538 .map(ExprBase::syntax)
539 .cloned()
540 .map(Into::into)
541 .as_ref(),
542 None,
543 None,
544 );
545 for suffix in suffixes {
546 p!(out, { suffix.value });
547 }
548 }
549}
550impl Printable for Suffix {
551 fn print(&self, out: &mut PrintItems) {
552 match self {
553 Self::SuffixIndex(i) => {
554 if i.question_mark_token().is_some() {
555 p!(out, str("?"));
556 }
557 p!(out, str(".") {i.index()});
558 }
559 Self::SuffixIndexExpr(e) => {
560 if e.question_mark_token().is_some() {
561 p!(out, str(".?"));
562 }
563 p!(out, str("[") {e.index()} str("]"));
564 }
565 Self::SuffixSlice(d) => {
566 p!(out, { d.slice_desc() });
567 }
568 Self::SuffixApply(a) => {
569 p!(out, { a.args_desc() });
570 }
571 }
572 }
573}
574impl Printable for Stmt {
575 fn print(&self, out: &mut PrintItems) {
576 match self {
577 Self::StmtLocal(l) => {
578 let (binds, end_comments) = children_between::<Bind>(
579 l.syntax().clone(),
580 l.local_kw_token().map(Into::into).as_ref(),
581 l.semi_token().map(Into::into).as_ref(),
582 None,
583 );
584 if binds.len() == 1 {
585 let bind = &binds[0];
586 format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);
587 p!(out, str("local ") {bind.value});
588 // TODO: keep end_comments, child.inline_trivia somehow, force multiple locals formatting in case of presence?
589 } else {
590 p!(out,str("local") >i nl);
591 for bind in binds {
592 if bind.should_start_with_newline {
593 p!(out, nl);
594 }
595 format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);
596 p!(out, {bind.value} str(","));
597 format_comments(&bind.inline_trivia, CommentLocation::ItemInline, out);
598 p!(out, nl);
599 }
600 if end_comments.should_start_with_newline {
601 p!(out, nl);
602 }
603 format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);
604 p!(out,<i);
605 }
606 p!(out,str(";") nl);
607 }
608 Self::StmtAssert(a) => {
609 p!(out, {a.assertion()} str(";") nl);
610 }
611 }
612 }
613}
614impl Printable for ExprBase {
615 fn print(&self, out: &mut PrintItems) {
616 match self {
617 Self::ExprBinary(b) => {
618 p!(out, {b.lhs_work()} str(" ") {b.binary_operator()} str(" ") {b.rhs_work()});
619 }
620 Self::ExprUnary(u) => p!(out, {u.unary_operator()} {u.rhs()}),
621 // Self::ExprSlice(s) => {
622 // p!(new: {s.expr()} {s.slice_desc()})
623 // }
624 // Self::ExprIndex(i) => {
625 // p!(new: {i.expr()} str(".") {i.index()})
626 // }
627 // Self::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),
628 // Self::ExprApply(a) => {
629 // let mut pi = p!(new: {a.expr()} {a.args_desc()});
630 // if a.tailstrict_kw_token().is_some() {
631 // p!(out,str(" tailstrict"));
632 // }
633 // pi
634 // }
635 Self::ExprObjExtend(ex) => {
636 p!(out, {ex.lhs_work()} str(" ") {ex.rhs_work()});
637 }
638 Self::ExprParened(p) => {
639 p!(out, str("(") {p.expr()} str(")"));
640 }
641 Self::ExprString(s) => p!(out, { s.text() }),
642 Self::ExprNumber(n) => p!(out, { n.number() }),
643 Self::ExprArray(a) => {
644 p!(out, str("[") >i nl);
645 for el in a.exprs() {
646 p!(out, {el} str(",") nl);
647 }
648 p!(out, <i str("]"));
649 }
650 Self::ExprObject(obj) => {
651 p!(out, { obj.obj_body() });
652 }
653 Self::ExprArrayComp(arr) => {
654 p!(out, str("[") {arr.expr()});
655 for spec in arr.comp_specs() {
656 p!(out, str(" ") {spec});
657 }
658 p!(out, str("]"));
659 }
660 Self::ExprImport(v) => {
661 p!(out, {v.import_kind()} str(" ") {v.text()});
662 }
663 Self::ExprVar(n) => p!(out, { n.name() }),
664 // Self::ExprLocal(l) => {
665 // }
666 Self::ExprIfThenElse(ite) => {
667 p!(out, str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});
668 if ite.else_kw_token().is_some() || ite.else_().is_some() {
669 p!(out, str(" else ") {ite.else_().map(|t| t.expr())});
670 }
671 }
672 Self::ExprFunction(f) => p!(out, str("function") {f.params_desc()} nl {f.expr()}),
673 // Self::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),
674 Self::ExprError(e) => p!(out, str("error ") {e.expr()}),
675 Self::ExprLiteral(l) => {
676 p!(out, { l.literal() });
677 }
678 }
679 }
680}
681
682impl Printable for SourceFile {
683 fn print(&self, out: &mut PrintItems) {
684 let before = trivia_before(
685 self.syntax().clone(),
686 self.expr()
687 .map(|e| e.syntax().clone())
688 .map(Into::into)
689 .as_ref(),
690 );
691 let after = trivia_after(
692 self.syntax().clone(),
693 self.expr()
694 .map(|e| e.syntax().clone())
695 .map(Into::into)
696 .as_ref(),
697 );
698 format_comments(&before, CommentLocation::AboveItem, out);
699 p!(out, {self.expr()} nl);
700 format_comments(&after, CommentLocation::EndOfItems, out);
701 }
702}
703
704struct FormatOptions {
705 // 0 for hard tabs
706 indent: u8,
707}
708fn format(input: &str, opts: &FormatOptions) -> Option<String> {
709 let (parsed, errors) = jrsonnet_rowan_parser::parse(input);
710 if !errors.is_empty() {
711 let mut builder = hi_doc::SnippetBuilder::new(input);
712 for error in errors {
713 builder
714 .error(hi_doc::Text::fragment(
715 format!("{:?}", error.error),
716 Formatting::default(),
717 ))
718 .range(
719 error.range.start().into()
720 ..=(usize::from(error.range.end()) - 1).max(error.range.start().into()),
721 )
722 .build();
723 }
724 let snippet = builder.build();
725 let ansi = hi_doc::source_to_ansi(&snippet);
726 eprintln!("{ansi}");
727 // It is possible to recover from this failure, but the output may be broken, as formatter is free to skip
728 // ERROR rowan nodes.
729 // Recovery needs to be enabled for LSP, though.
730 //
731 // TODO: Verify how formatter interacts in cases of missing positional values, i.e `if cond then /*missing Expr*/ else residual`.
732 return None;
733 }
734 Some(dprint_core::formatting::format(
735 || {
736 let mut out = PrintItems::new();
737 parsed.print(&mut out);
738 out
739 },
740 PrintOptions {
741 indent_width: if opts.indent == 0 {
742 // Reasonable max length for both 2 and 4 space sized tabs.
743 3
744 } else {
745 opts.indent
746 },
747 max_width: 100,
748 use_tabs: opts.indent == 0,
749 new_line_text: "\n",
750 },
751 ))
752}
7535
754#[derive(Parser)]6#[derive(Parser)]
755#[allow(clippy::struct_excessive_bools)]7#[allow(clippy::struct_excessive_bools)]
768 #[arg(long)]20 #[arg(long)]
769 test: bool,21 test: bool,
770 /// Number of spaces to indent with22 /// Number of spaces to indent with
771 ///
772 /// 0 for guess from input (default), and use hard tabs if unable to guess.
773 #[arg(long, default_value = "0")]23 #[arg(long, default_value = "2")]
774 indent: u8,24 indent: u8,
775 /// Force hard tab for indentation25 /// Force hard tab for indentation
776 #[arg(long)]26 #[arg(long)]
819 let mut convergence_tmp;69 let mut convergence_tmp;
820 // https://github.com/dprint/dprint/pull/42370 // https://github.com/dprint/dprint/pull/423
821 loop {71 loop {
822 let Some(reformatted) = format(72 let reformatted = match format(
823 &formatted,73 &formatted,
824 &FormatOptions {74 &FormatOptions {
825 indent: if opts.indent == 0 || opts.hard_tabs {75 indent: if opts.indent == 0 || opts.hard_tabs {
828 opts.indent78 opts.indent
829 },79 },
830 },80 },
831 ) else {81 ) {
82 Ok(v) => v,
83 Err(e) => {
84 let snippet = e.build();
85 let ansi = hi_doc::source_to_ansi(&snippet);
86 eprintln!("{ansi}");
832 return Err(Error::Parse);87 return Err(Error::Parse);
88 }
833 };89 };
834 convergence_tmp = reformatted.trim().to_owned();90 convergence_tmp = reformatted.trim().to_owned();
835 if formatted == convergence_tmp {91 if formatted == convergence_tmp {
deletedcmds/jrsonnet-fmt/src/snapshots/jrsonnet_fmt__tests__complex_comments_snapshot.snapdiffbeforeafterboth

no changes

deletedcmds/jrsonnet-fmt/src/tests.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-formatter/Cargo.tomldiffbeforeafterboth

no changes

addedcrates/jrsonnet-formatter/src/children.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-formatter/src/comments.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-formatter/src/lib.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-formatter/src/snapshots/jrsonnet_fmt__tests__complex_comments_snapshot.snapdiffbeforeafterboth

no changes

addedcrates/jrsonnet-formatter/src/tests.rsdiffbeforeafterboth

no changes