1use bock_ast::{
4 Annotation, Arg, AssignOp, BinOp, Block, ClassDecl, ConstDecl, EffectDecl, EnumDecl,
5 EnumVariant, Expr, FnDecl, ForLoop, GenericParam, GuardStmt, HandlingBlock, ImplBlock,
6 ImportDecl, ImportItems, ImportedName, InterpolationPart, Item, LetStmt, Literal, LoopStmt,
7 MatchArm, Module, ModuleHandleDecl, ModulePath, Param, Pattern, PropertyTestDecl, RecordDecl,
8 RecordDeclField, Stmt, TraitDecl, TypeAliasDecl, TypeConstraint, TypeExpr, TypePath, UnaryOp,
9 Visibility, WhileLoop,
10};
11
12use crate::comments::{Comment, CommentKind};
13
14const SOFT_LIMIT: usize = 80;
16
17const HARD_LIMIT: usize = 100;
19
20const INDENT_WIDTH: usize = 2;
22
23pub struct Formatter<'a> {
25 buf: String,
27 indent: usize,
29 comments: &'a [Comment],
31 next_comment: usize,
33 source: &'a str,
35}
36
37impl<'a> Formatter<'a> {
38 #[must_use]
40 pub fn new(comments: &'a [Comment], source: &'a str) -> Self {
41 Self {
42 buf: String::with_capacity(source.len()),
43 indent: 0,
44 comments,
45 next_comment: 0,
46 source,
47 }
48 }
49
50 #[must_use]
52 pub fn finish(mut self) -> String {
53 self.emit_remaining_comments();
55 let trimmed = self.buf.trim_end().to_string();
57 if trimmed.is_empty() {
58 return String::new();
59 }
60 self.buf = trimmed;
61 self.buf.push('\n');
62 wrap_long_lines(&self.buf)
64 }
65
66 fn indent_str(&self) -> String {
69 " ".repeat(self.indent * INDENT_WIDTH)
70 }
71
72 fn push_indent(&mut self) {
73 let indent = self.indent_str();
74 self.buf.push_str(&indent);
75 }
76
77 fn inc_indent(&mut self) {
78 self.indent += 1;
79 }
80
81 fn dec_indent(&mut self) {
82 self.indent = self.indent.saturating_sub(1);
83 }
84
85 fn push(&mut self, s: &str) {
88 self.buf.push_str(s);
89 }
90
91 fn push_char(&mut self, c: char) {
92 self.buf.push(c);
93 }
94
95 fn newline(&mut self) {
96 self.buf.push('\n');
97 }
98
99 fn push_line(&mut self, s: &str) {
100 self.push_indent();
101 self.push(s);
102 self.newline();
103 }
104
105 fn estimate_expr_len(&self, expr: &Expr) -> usize {
107 let mut f = Formatter::new(&[], "");
108 f.format_expr(expr);
109 f.buf.len()
110 }
111
112 fn estimate_type_len(&self, ty: &TypeExpr) -> usize {
114 let mut f = Formatter::new(&[], "");
115 f.format_type_expr(ty);
116 f.buf.len()
117 }
118
119 fn sig_fits_one_line(&self, decl: &FnDecl) -> bool {
121 let mut est = 0;
122 est += 3 + decl.name.name.len() + 1;
124 for (i, p) in decl.params.iter().enumerate() {
125 if i > 0 {
126 est += 2; }
128 est += self.estimate_param_len(p);
129 }
130 est += 1; if let Some(ret) = &decl.return_type {
132 est += 4; est += self.estimate_type_len(ret);
134 }
135 est += 2; self.indent * INDENT_WIDTH + est <= SOFT_LIMIT
137 }
138
139 fn estimate_param_len(&self, p: &Param) -> usize {
140 let mut est = self.estimate_pattern_len(&p.pattern);
141 if let Some(ty) = &p.ty {
142 est += 2; est += self.estimate_type_len(ty);
144 }
145 if let Some(def) = &p.default {
146 est += 3; est += self.estimate_expr_len(def);
148 }
149 est
150 }
151
152 fn estimate_pattern_len(&self, pat: &Pattern) -> usize {
153 let mut f = Formatter::new(&[], "");
154 f.format_pattern(pat);
155 f.buf.len()
156 }
157
158 fn emit_comments_before(&mut self, byte_offset: usize) {
163 while self.next_comment < self.comments.len() {
164 let c = &self.comments[self.next_comment];
165 if c.start >= byte_offset {
166 break;
167 }
168 if c.kind == CommentKind::Doc || c.kind == CommentKind::ModuleDoc {
170 self.next_comment += 1;
171 continue;
172 }
173
174 let is_inline = c.start > 0 && {
176 let before = &self.source[..c.start];
177 let last_nl = before.rfind('\n').map_or(0, |p| p + 1);
178 let line_before = before[last_nl..].trim();
179 !line_before.is_empty()
180 };
181
182 if is_inline {
183 let trimmed_end = self.buf.trim_end_matches(' ').len();
186 self.buf.truncate(trimmed_end);
187 self.push(" ");
188 self.push(&c.text);
189 } else {
190 self.push_indent();
192 self.push(&c.text);
193 self.newline();
194 }
195 self.next_comment += 1;
196 }
197 }
198
199 fn emit_remaining_comments(&mut self) {
201 while self.next_comment < self.comments.len() {
202 let c = &self.comments[self.next_comment];
203 if c.kind == CommentKind::Doc || c.kind == CommentKind::ModuleDoc {
204 self.next_comment += 1;
205 continue;
206 }
207 self.newline();
208 self.push_indent();
209 self.push(&c.text);
210 self.next_comment += 1;
211 }
212 }
213
214 pub fn format_module(&mut self, module: &Module) {
218 for doc in &module.doc {
220 self.push("//! ");
221 self.push(doc);
222 self.newline();
223 }
224 if !module.doc.is_empty() {
225 self.newline();
226 }
227
228 if let Some(path) = &module.path {
230 self.push("module ");
231 self.format_module_path(path);
232 self.newline();
233 self.newline();
234 }
235
236 if !module.imports.is_empty() {
238 let mut sorted_imports = module.imports.clone();
239 sorted_imports.sort_by(|a, b| {
240 let cat_a = import_category(a);
241 let cat_b = import_category(b);
242 cat_a
243 .cmp(&cat_b)
244 .then_with(|| import_path_str(a).cmp(&import_path_str(b)))
245 });
246
247 let mut prev_cat = None;
249 for import in &sorted_imports {
250 let cat = import_category(import);
251 if let Some(prev) = prev_cat {
252 if prev != cat {
253 self.newline();
254 }
255 }
256 self.emit_comments_before(import.span.start);
257 self.format_import(import);
258 prev_cat = Some(cat);
259 }
260 self.newline();
261 }
262
263 let mut first = true;
265 for item in &module.items {
266 if !first {
267 self.newline();
268 }
269 self.emit_comments_before(item.span().start);
270 self.format_item(item);
271 first = false;
272 }
273 }
274
275 fn format_import(&mut self, import: &ImportDecl) {
278 self.push("use ");
279 self.format_module_path(&import.path);
280 match &import.items {
281 ImportItems::Module => {}
282 ImportItems::Glob => {
283 self.push(".*");
284 }
285 ImportItems::Named(names) => {
286 self.push(".{ ");
287 for (i, name) in names.iter().enumerate() {
288 if i > 0 {
289 self.push(", ");
290 }
291 self.format_imported_name(name);
292 }
293 self.push(" }");
294 }
295 }
296 self.newline();
297 }
298
299 fn format_imported_name(&mut self, name: &ImportedName) {
300 self.push(&name.name.name);
301 if let Some(alias) = &name.alias {
302 self.push(" as ");
303 self.push(&alias.name);
304 }
305 }
306
307 fn format_module_path(&mut self, path: &ModulePath) {
308 for (i, seg) in path.segments.iter().enumerate() {
309 if i > 0 {
310 self.push_char('.');
311 }
312 self.push(&seg.name);
313 }
314 }
315
316 fn format_item(&mut self, item: &Item) {
319 match item {
320 Item::Fn(decl) => self.format_fn_decl(decl),
321 Item::Record(decl) => self.format_record_decl(decl),
322 Item::Enum(decl) => self.format_enum_decl(decl),
323 Item::Class(decl) => self.format_class_decl(decl),
324 Item::Trait(decl) | Item::PlatformTrait(decl) => {
325 self.format_trait_decl(decl);
326 }
327 Item::Impl(decl) => self.format_impl_block(decl),
328 Item::Effect(decl) => self.format_effect_decl(decl),
329 Item::TypeAlias(decl) => self.format_type_alias(decl),
330 Item::Const(decl) => self.format_const_decl(decl),
331 Item::ModuleHandle(decl) => self.format_module_handle(decl),
332 Item::PropertyTest(decl) => self.format_property_test(decl),
333 Item::Error { .. } => {}
334 }
335 }
336
337 fn format_annotations(&mut self, annotations: &[Annotation]) {
340 for ann in annotations {
341 self.push_indent();
342 self.push_char('@');
343 self.push(&ann.name.name);
344 if !ann.args.is_empty() {
345 self.push_char('(');
346 for (i, arg) in ann.args.iter().enumerate() {
347 if i > 0 {
348 self.push(", ");
349 }
350 if let Some(label) = &arg.label {
351 self.push(&label.name);
352 self.push(": ");
353 }
354 self.format_expr(&arg.value);
355 }
356 self.push_char(')');
357 }
358 self.newline();
359 }
360 }
361
362 fn format_visibility(&mut self, vis: Visibility) {
365 match vis {
366 Visibility::Private => {}
367 Visibility::Internal => self.push("internal "),
368 Visibility::Public => self.push("pub "),
369 }
370 }
371
372 fn format_fn_decl(&mut self, decl: &FnDecl) {
375 self.format_annotations(&decl.annotations);
378 self.push_indent();
379 self.format_visibility(decl.visibility);
380 if decl.is_async {
381 self.push("async ");
382 }
383 self.push("fn ");
384 self.push(&decl.name.name);
385
386 self.format_generic_params(&decl.generic_params);
388
389 if self.sig_fits_one_line(decl) {
390 self.push_char('(');
392 for (i, p) in decl.params.iter().enumerate() {
393 if i > 0 {
394 self.push(", ");
395 }
396 self.format_param(p);
397 }
398 self.push_char(')');
399 } else {
400 self.push_char('(');
402 self.newline();
403 self.inc_indent();
404 for (i, p) in decl.params.iter().enumerate() {
405 self.push_indent();
406 self.format_param(p);
407 if i < decl.params.len() - 1 {
408 self.push_char(',');
409 } else {
410 self.push_char(','); }
412 self.newline();
413 }
414 self.dec_indent();
415 self.push_indent();
416 self.push_char(')');
417 }
418
419 if let Some(ret) = &decl.return_type {
421 self.push(" -> ");
422 self.format_type_expr(ret);
423 }
424
425 if !decl.effect_clause.is_empty() {
427 self.push(" with ");
428 for (i, eff) in decl.effect_clause.iter().enumerate() {
429 if i > 0 {
430 self.push(", ");
431 }
432 self.format_type_path(eff);
433 }
434 }
435
436 if !decl.where_clause.is_empty() {
438 self.newline();
439 self.inc_indent();
440 self.push_indent();
441 self.push("where ");
442 for (i, c) in decl.where_clause.iter().enumerate() {
443 if i > 0 {
444 self.push(", ");
445 }
446 self.format_type_constraint(c);
447 }
448 self.dec_indent();
449 }
450
451 if let Some(body) = &decl.body {
452 self.push(" ");
453 self.format_block(body);
454 }
455 self.newline();
456 }
457
458 fn format_param(&mut self, param: &Param) {
459 self.format_pattern(¶m.pattern);
460 if let Some(ty) = ¶m.ty {
461 self.push(": ");
462 self.format_type_expr(ty);
463 }
464 if let Some(def) = ¶m.default {
465 self.push(" = ");
466 self.format_expr(def);
467 }
468 }
469
470 fn format_generic_params(&mut self, params: &[GenericParam]) {
471 if params.is_empty() {
472 return;
473 }
474 self.push_char('[');
475 for (i, p) in params.iter().enumerate() {
476 if i > 0 {
477 self.push(", ");
478 }
479 self.push(&p.name.name);
480 if !p.bounds.is_empty() {
481 self.push(": ");
482 for (j, b) in p.bounds.iter().enumerate() {
483 if j > 0 {
484 self.push(" + ");
485 }
486 self.format_type_path(b);
487 }
488 }
489 }
490 self.push_char(']');
491 }
492
493 fn format_type_constraint(&mut self, c: &TypeConstraint) {
494 self.push(&c.param.name);
495 self.push(": ");
496 for (i, b) in c.bounds.iter().enumerate() {
497 if i > 0 {
498 self.push(" + ");
499 }
500 self.format_type_path(b);
501 }
502 }
503
504 fn format_record_decl(&mut self, decl: &RecordDecl) {
507 self.format_annotations(&decl.annotations);
508 self.push_indent();
509 self.format_visibility(decl.visibility);
510 self.push("record ");
511 self.push(&decl.name.name);
512 self.format_generic_params(&decl.generic_params);
513 self.push(" {");
514 self.newline();
515 self.inc_indent();
516 for field in &decl.fields {
517 self.emit_comments_before(field.span.start);
518 self.format_record_field_decl(field);
519 }
520 self.dec_indent();
521 self.push_line("}");
522 }
523
524 fn format_record_field_decl(&mut self, field: &RecordDeclField) {
525 self.push_indent();
526 self.push(&field.name.name);
527 self.push(": ");
528 self.format_type_expr(&field.ty);
529 if let Some(def) = &field.default {
530 self.push(" = ");
531 self.format_expr(def);
532 }
533 self.push_char(',');
534 self.newline();
535 }
536
537 fn format_enum_decl(&mut self, decl: &EnumDecl) {
540 self.format_annotations(&decl.annotations);
541 self.push_indent();
542 self.format_visibility(decl.visibility);
543 self.push("enum ");
544 self.push(&decl.name.name);
545 self.format_generic_params(&decl.generic_params);
546 self.push(" {");
547 self.newline();
548 self.inc_indent();
549 for variant in &decl.variants {
550 self.format_enum_variant(variant);
551 }
552 self.dec_indent();
553 self.push_line("}");
554 }
555
556 fn format_enum_variant(&mut self, variant: &EnumVariant) {
557 self.push_indent();
558 match variant {
559 EnumVariant::Unit { name, .. } => {
560 self.push(&name.name);
561 self.push_char(',');
562 }
563 EnumVariant::Tuple { name, tys, .. } => {
564 self.push(&name.name);
565 self.push_char('(');
566 for (i, ty) in tys.iter().enumerate() {
567 if i > 0 {
568 self.push(", ");
569 }
570 self.format_type_expr(ty);
571 }
572 self.push("),");
573 }
574 EnumVariant::Struct { name, fields, .. } => {
575 self.push(&name.name);
576 self.push(" {");
577 self.newline();
578 self.inc_indent();
579 for field in fields {
580 self.format_record_field_decl(field);
581 }
582 self.dec_indent();
583 self.push_indent();
584 self.push("},");
585 }
586 }
587 self.newline();
588 }
589
590 fn format_class_decl(&mut self, decl: &ClassDecl) {
593 self.format_annotations(&decl.annotations);
594 self.push_indent();
595 self.format_visibility(decl.visibility);
596 self.push("class ");
597 self.push(&decl.name.name);
598 self.format_generic_params(&decl.generic_params);
599 if let Some(base) = &decl.base {
600 self.push(" extends ");
601 self.format_type_path(base);
602 }
603 if !decl.traits.is_empty() {
604 self.push(" impl ");
605 for (i, t) in decl.traits.iter().enumerate() {
606 if i > 0 {
607 self.push(", ");
608 }
609 self.format_type_path(t);
610 }
611 }
612 self.push(" {");
613 self.newline();
614 self.inc_indent();
615 for field in &decl.fields {
616 self.format_record_field_decl(field);
617 }
618 if !decl.fields.is_empty() && !decl.methods.is_empty() {
619 self.newline();
620 }
621 for (i, method) in decl.methods.iter().enumerate() {
622 if i > 0 {
623 self.newline();
624 }
625 self.format_fn_decl(method);
626 }
627 self.dec_indent();
628 self.push_line("}");
629 }
630
631 fn format_trait_decl(&mut self, decl: &TraitDecl) {
634 self.format_annotations(&decl.annotations);
635 self.push_indent();
636 self.format_visibility(decl.visibility);
637 if decl.is_platform {
638 self.push("platform ");
639 }
640 self.push("trait ");
641 self.push(&decl.name.name);
642 self.format_generic_params(&decl.generic_params);
643 self.push(" {");
644 self.newline();
645 self.inc_indent();
646 for assoc in &decl.associated_types {
647 self.push_indent();
648 self.push("type ");
649 self.push(&assoc.name.name);
650 if !assoc.bounds.is_empty() {
651 self.push(": ");
652 for (i, b) in assoc.bounds.iter().enumerate() {
653 if i > 0 {
654 self.push(" + ");
655 }
656 self.format_type_path(b);
657 }
658 }
659 self.newline();
660 }
661 if !decl.associated_types.is_empty() && !decl.methods.is_empty() {
662 self.newline();
663 }
664 for (i, method) in decl.methods.iter().enumerate() {
665 if i > 0 {
666 self.newline();
667 }
668 self.format_fn_decl(method);
669 }
670 self.dec_indent();
671 self.push_line("}");
672 }
673
674 fn format_impl_block(&mut self, decl: &ImplBlock) {
677 self.format_annotations(&decl.annotations);
678 self.push_indent();
679 self.push("impl ");
680 self.format_generic_params(&decl.generic_params);
681 if !decl.generic_params.is_empty() {
682 self.push_char(' ');
683 }
684 if let Some(trait_path) = &decl.trait_path {
685 self.format_type_path(trait_path);
686 self.push(" for ");
687 }
688 self.format_type_expr(&decl.target);
689 if !decl.where_clause.is_empty() {
690 self.newline();
691 self.inc_indent();
692 self.push_indent();
693 self.push("where ");
694 for (i, c) in decl.where_clause.iter().enumerate() {
695 if i > 0 {
696 self.push(", ");
697 }
698 self.format_type_constraint(c);
699 }
700 self.dec_indent();
701 }
702 self.push(" {");
703 self.newline();
704 self.inc_indent();
705 for (i, method) in decl.methods.iter().enumerate() {
706 if i > 0 {
707 self.newline();
708 }
709 self.format_fn_decl(method);
710 }
711 self.dec_indent();
712 self.push_line("}");
713 }
714
715 fn format_effect_decl(&mut self, decl: &EffectDecl) {
718 self.format_annotations(&decl.annotations);
719 self.push_indent();
720 self.format_visibility(decl.visibility);
721 self.push("effect ");
722 self.push(&decl.name.name);
723 self.format_generic_params(&decl.generic_params);
724 self.push(" {");
725 self.newline();
726 self.inc_indent();
727 for (i, op) in decl.operations.iter().enumerate() {
728 if i > 0 {
729 self.newline();
730 }
731 self.format_fn_decl(op);
732 }
733 self.dec_indent();
734 self.push_line("}");
735 }
736
737 fn format_type_alias(&mut self, decl: &TypeAliasDecl) {
740 self.format_annotations(&decl.annotations);
741 self.push_indent();
742 self.format_visibility(decl.visibility);
743 self.push("type ");
744 self.push(&decl.name.name);
745 self.format_generic_params(&decl.generic_params);
746 self.push(" = ");
747 self.format_type_expr(&decl.ty);
748 if !decl.where_clause.is_empty() {
749 self.push(" where ");
750 for (i, c) in decl.where_clause.iter().enumerate() {
751 if i > 0 {
752 self.push(", ");
753 }
754 self.format_type_constraint(c);
755 }
756 }
757 self.newline();
758 }
759
760 fn format_const_decl(&mut self, decl: &ConstDecl) {
763 self.format_annotations(&decl.annotations);
764 self.push_indent();
765 self.format_visibility(decl.visibility);
766 self.push("const ");
767 self.push(&decl.name.name);
768 self.push(": ");
769 self.format_type_expr(&decl.ty);
770 self.push(" = ");
771 self.format_expr(&decl.value);
772 self.newline();
773 }
774
775 fn format_module_handle(&mut self, decl: &ModuleHandleDecl) {
778 self.push_indent();
779 self.push("handle ");
780 self.format_type_path(&decl.effect);
781 self.push(" with ");
782 self.format_expr(&decl.handler);
783 self.newline();
784 }
785
786 fn format_property_test(&mut self, decl: &PropertyTestDecl) {
789 self.push_indent();
790 self.push("property(\"");
791 self.push(&decl.name);
792 self.push("\") {");
793 self.newline();
794 self.inc_indent();
795 if !decl.bindings.is_empty() {
796 self.push_indent();
797 self.push("forall(");
798 for (i, b) in decl.bindings.iter().enumerate() {
799 if i > 0 {
800 self.push(", ");
801 }
802 self.push(&b.name.name);
803 self.push(": ");
804 self.format_type_expr(&b.ty);
805 }
806 self.push(") ");
807 self.format_block(&decl.body);
808 self.newline();
809 } else {
810 self.format_block_body(&decl.body);
811 }
812 self.dec_indent();
813 self.push_line("}");
814 }
815
816 fn format_type_expr(&mut self, ty: &TypeExpr) {
819 match ty {
820 TypeExpr::Named { path, args, .. } => {
821 self.format_type_path(path);
822 if !args.is_empty() {
823 self.push_char('[');
824 for (i, arg) in args.iter().enumerate() {
825 if i > 0 {
826 self.push(", ");
827 }
828 self.format_type_expr(arg);
829 }
830 self.push_char(']');
831 }
832 }
833 TypeExpr::Tuple { elems, .. } => {
834 self.push_char('(');
835 for (i, elem) in elems.iter().enumerate() {
836 if i > 0 {
837 self.push(", ");
838 }
839 self.format_type_expr(elem);
840 }
841 self.push_char(')');
842 }
843 TypeExpr::Function {
844 params,
845 ret,
846 effects,
847 ..
848 } => {
849 self.push("Fn(");
850 for (i, p) in params.iter().enumerate() {
851 if i > 0 {
852 self.push(", ");
853 }
854 self.format_type_expr(p);
855 }
856 self.push(") -> ");
857 self.format_type_expr(ret);
858 if !effects.is_empty() {
859 self.push(" with ");
860 for (i, eff) in effects.iter().enumerate() {
861 if i > 0 {
862 self.push(", ");
863 }
864 self.format_type_path(eff);
865 }
866 }
867 }
868 TypeExpr::Optional { inner, .. } => {
869 self.format_type_expr(inner);
870 self.push_char('?');
871 }
872 TypeExpr::SelfType { .. } => {
873 self.push("Self");
874 }
875 }
876 }
877
878 fn format_type_path(&mut self, path: &TypePath) {
879 for (i, seg) in path.segments.iter().enumerate() {
880 if i > 0 {
881 self.push_char('.');
882 }
883 self.push(&seg.name);
884 }
885 }
886
887 fn format_pattern(&mut self, pat: &Pattern) {
890 match pat {
891 Pattern::Wildcard { .. } => self.push_char('_'),
892 Pattern::Bind { name, .. } => self.push(&name.name),
893 Pattern::MutBind { name, .. } => {
894 self.push("mut ");
895 self.push(&name.name);
896 }
897 Pattern::Literal { lit, .. } => self.format_literal(lit),
898 Pattern::Constructor { path, fields, .. } => {
899 self.format_type_path(path);
900 self.push_char('(');
901 for (i, f) in fields.iter().enumerate() {
902 if i > 0 {
903 self.push(", ");
904 }
905 self.format_pattern(f);
906 }
907 self.push_char(')');
908 }
909 Pattern::Record {
910 path, fields, rest, ..
911 } => {
912 self.format_type_path(path);
913 self.push(" { ");
914 for (i, f) in fields.iter().enumerate() {
915 if i > 0 {
916 self.push(", ");
917 }
918 self.push(&f.name.name);
919 if let Some(p) = &f.pattern {
920 self.push(": ");
921 self.format_pattern(p);
922 }
923 }
924 if *rest {
925 if !fields.is_empty() {
926 self.push(", ");
927 }
928 self.push("..");
929 }
930 self.push(" }");
931 }
932 Pattern::Tuple { elems, .. } => {
933 self.push_char('(');
934 for (i, e) in elems.iter().enumerate() {
935 if i > 0 {
936 self.push(", ");
937 }
938 self.format_pattern(e);
939 }
940 self.push_char(')');
941 }
942 Pattern::List { elems, rest, .. } => {
943 self.push_char('[');
944 for (i, e) in elems.iter().enumerate() {
945 if i > 0 {
946 self.push(", ");
947 }
948 self.format_pattern(e);
949 }
950 if let Some(r) = rest {
951 if !elems.is_empty() {
952 self.push(", ");
953 }
954 self.push("..");
955 self.format_pattern(r);
956 }
957 self.push_char(']');
958 }
959 Pattern::Or { alternatives, .. } => {
960 for (i, alt) in alternatives.iter().enumerate() {
961 if i > 0 {
962 self.push(" | ");
963 }
964 self.format_pattern(alt);
965 }
966 }
967 Pattern::Range {
968 lo, hi, inclusive, ..
969 } => {
970 self.format_pattern(lo);
971 if *inclusive {
972 self.push("..=");
973 } else {
974 self.push("..");
975 }
976 self.format_pattern(hi);
977 }
978 Pattern::Rest { .. } => {
979 self.push("..");
980 }
981 }
982 }
983
984 fn format_expr(&mut self, expr: &Expr) {
987 match expr {
988 Expr::Literal { lit, .. } => self.format_literal(lit),
989 Expr::Identifier { name, .. } => self.push(&name.name),
990 Expr::Binary {
991 op, left, right, ..
992 } => {
993 self.format_expr_maybe_paren(left, expr);
994 self.push_char(' ');
995 self.push(binop_str(*op));
996 self.push_char(' ');
997 self.format_expr_maybe_paren(right, expr);
998 }
999 Expr::Unary { op, operand, .. } => {
1000 self.push(unaryop_str(*op));
1001 self.format_expr(operand);
1002 }
1003 Expr::Assign {
1004 op, target, value, ..
1005 } => {
1006 self.format_expr(target);
1007 self.push_char(' ');
1008 self.push(assignop_str(*op));
1009 self.push_char(' ');
1010 self.format_expr(value);
1011 }
1012 Expr::Call {
1013 callee,
1014 args,
1015 type_args,
1016 ..
1017 } => {
1018 self.format_expr(callee);
1019 if !type_args.is_empty() {
1020 self.push_char('[');
1021 for (i, ta) in type_args.iter().enumerate() {
1022 if i > 0 {
1023 self.push(", ");
1024 }
1025 self.format_type_expr(ta);
1026 }
1027 self.push_char(']');
1028 }
1029 self.push_char('(');
1030 self.format_args(args);
1031 self.push_char(')');
1032 }
1033 Expr::MethodCall {
1034 receiver,
1035 method,
1036 type_args,
1037 args,
1038 ..
1039 } => {
1040 self.format_expr(receiver);
1041 self.push_char('.');
1042 self.push(&method.name);
1043 if !type_args.is_empty() {
1044 self.push_char('[');
1045 for (i, ta) in type_args.iter().enumerate() {
1046 if i > 0 {
1047 self.push(", ");
1048 }
1049 self.format_type_expr(ta);
1050 }
1051 self.push_char(']');
1052 }
1053 self.push_char('(');
1054 self.format_args(args);
1055 self.push_char(')');
1056 }
1057 Expr::FieldAccess { object, field, .. } => {
1058 self.format_expr(object);
1059 self.push_char('.');
1060 self.push(&field.name);
1061 }
1062 Expr::Index { object, index, .. } => {
1063 self.format_expr(object);
1064 self.push_char('[');
1065 self.format_expr(index);
1066 self.push_char(']');
1067 }
1068 Expr::Try { expr: inner, .. } => {
1069 self.format_expr(inner);
1070 self.push_char('?');
1071 }
1072 Expr::Lambda { params, body, .. } => {
1073 self.push_char('(');
1074 for (i, p) in params.iter().enumerate() {
1075 if i > 0 {
1076 self.push(", ");
1077 }
1078 self.format_param(p);
1079 }
1080 self.push(") => ");
1081 self.format_expr(body);
1082 }
1083 Expr::Pipe { left, right, .. } => {
1084 self.format_expr(left);
1085 self.newline();
1086 self.push_indent();
1087 self.push("|> ");
1088 self.format_expr(right);
1089 }
1090 Expr::Compose { left, right, .. } => {
1091 self.format_expr(left);
1092 self.push(" >> ");
1093 self.format_expr(right);
1094 }
1095 Expr::If {
1096 let_pattern,
1097 condition,
1098 then_block,
1099 else_block,
1100 ..
1101 } => {
1102 self.push("if ");
1103 if let Some(pat) = let_pattern {
1104 self.push("let ");
1105 self.format_pattern(pat);
1106 self.push(" = ");
1107 }
1108 self.format_expr(condition);
1109 self.push(" ");
1110 self.format_block(then_block);
1111 if let Some(else_expr) = else_block {
1112 self.push(" else ");
1113 match else_expr.as_ref() {
1114 Expr::If { .. } => {
1115 self.format_expr(else_expr);
1116 }
1117 Expr::Block { block, .. } => {
1118 self.format_block(block);
1119 }
1120 _ => {
1121 self.format_expr(else_expr);
1122 }
1123 }
1124 }
1125 }
1126 Expr::Match {
1127 scrutinee, arms, ..
1128 } => {
1129 self.push("match ");
1130 self.format_expr(scrutinee);
1131 self.push(" {");
1132 self.newline();
1133 self.inc_indent();
1134 for arm in arms {
1135 self.format_match_arm(arm);
1136 }
1137 self.dec_indent();
1138 self.push_indent();
1139 self.push_char('}');
1140 }
1141 Expr::Loop { body, .. } => {
1142 self.push("loop ");
1143 self.format_block(body);
1144 }
1145 Expr::Block { block, .. } => {
1146 self.format_block(block);
1147 }
1148 Expr::RecordConstruct {
1149 path,
1150 fields,
1151 spread,
1152 ..
1153 } => {
1154 self.format_type_path(path);
1155 self.push(" {");
1156 if fields.is_empty() && spread.is_none() {
1157 self.push("}");
1158 } else {
1159 self.newline();
1160 self.inc_indent();
1161 for field in fields {
1162 self.push_indent();
1163 self.push(&field.name.name);
1164 if let Some(val) = &field.value {
1165 self.push(": ");
1166 self.format_expr(val);
1167 }
1168 self.push_char(',');
1169 self.newline();
1170 }
1171 if let Some(spr) = spread {
1172 self.push_indent();
1173 self.push("..");
1174 self.format_expr(&spr.expr);
1175 self.push_char(',');
1176 self.newline();
1177 }
1178 self.dec_indent();
1179 self.push_indent();
1180 self.push_char('}');
1181 }
1182 }
1183 Expr::ListLiteral { elems, .. } => {
1184 self.format_collection('[', ']', elems);
1185 }
1186 Expr::MapLiteral { entries, .. } => {
1187 if entries.is_empty() {
1188 self.push("{}");
1189 } else {
1190 self.push("{");
1191 self.newline();
1192 self.inc_indent();
1193 for (k, v) in entries {
1194 self.push_indent();
1195 self.format_expr(k);
1196 self.push(": ");
1197 self.format_expr(v);
1198 self.push_char(',');
1199 self.newline();
1200 }
1201 self.dec_indent();
1202 self.push_indent();
1203 self.push("}");
1204 }
1205 }
1206 Expr::SetLiteral { elems, .. } => {
1207 if elems.is_empty() {
1208 self.push("#{}");
1209 } else {
1210 self.push("#{");
1211 for (i, e) in elems.iter().enumerate() {
1212 if i > 0 {
1213 self.push(", ");
1214 }
1215 self.format_expr(e);
1216 }
1217 self.push("}");
1218 }
1219 }
1220 Expr::TupleLiteral { elems, .. } => {
1221 self.push_char('(');
1222 for (i, e) in elems.iter().enumerate() {
1223 if i > 0 {
1224 self.push(", ");
1225 }
1226 self.format_expr(e);
1227 }
1228 self.push_char(')');
1229 }
1230 Expr::Range {
1231 lo, hi, inclusive, ..
1232 } => {
1233 self.format_expr(lo);
1234 if *inclusive {
1235 self.push("..=");
1236 } else {
1237 self.push("..");
1238 }
1239 self.format_expr(hi);
1240 }
1241 Expr::Await { expr: inner, .. } => {
1242 self.push("await ");
1243 self.format_expr(inner);
1244 }
1245 Expr::Return { value, .. } => {
1246 self.push("return");
1247 if let Some(val) = value {
1248 self.push_char(' ');
1249 self.format_expr(val);
1250 }
1251 }
1252 Expr::Break { value, .. } => {
1253 self.push("break");
1254 if let Some(val) = value {
1255 self.push_char(' ');
1256 self.format_expr(val);
1257 }
1258 }
1259 Expr::Continue { .. } => {
1260 self.push("continue");
1261 }
1262 Expr::Unreachable { .. } => {
1263 self.push("unreachable");
1264 }
1265 Expr::Interpolation { parts, .. } => {
1266 self.push_char('"');
1267 for part in parts {
1268 match part {
1269 InterpolationPart::Literal(s) => self.push(s),
1270 InterpolationPart::Expr(e) => {
1271 self.push("${");
1272 self.format_expr(e);
1273 self.push_char('}');
1274 }
1275 }
1276 }
1277 self.push_char('"');
1278 }
1279 Expr::Placeholder { .. } => {
1280 self.push_char('_');
1281 }
1282 Expr::Is {
1283 expr, type_expr, ..
1284 } => {
1285 self.format_expr(expr);
1286 self.push(" is ");
1287 self.format_type_expr(type_expr);
1288 }
1289 }
1290 }
1291
1292 fn format_expr_maybe_paren(&mut self, inner: &Expr, _parent: &Expr) {
1294 match inner {
1298 Expr::Binary { .. } | Expr::Assign { .. } => {
1299 self.format_expr(inner);
1302 }
1303 _ => self.format_expr(inner),
1304 }
1305 }
1306
1307 fn format_args(&mut self, args: &[Arg]) {
1308 for (i, arg) in args.iter().enumerate() {
1309 if i > 0 {
1310 self.push(", ");
1311 }
1312 if arg.mutable {
1313 self.push("mut ");
1314 }
1315 if let Some(label) = &arg.label {
1316 self.push(&label.name);
1317 self.push(": ");
1318 }
1319 self.format_expr(&arg.value);
1320 }
1321 }
1322
1323 fn format_collection(&mut self, open: char, close: char, elems: &[Expr]) {
1324 self.push_char(open);
1325 let total_est: usize = elems.iter().map(|e| self.estimate_expr_len(e) + 2).sum();
1327 if total_est + self.indent * INDENT_WIDTH <= SOFT_LIMIT || elems.is_empty() {
1328 for (i, e) in elems.iter().enumerate() {
1329 if i > 0 {
1330 self.push(", ");
1331 }
1332 self.format_expr(e);
1333 }
1334 } else {
1335 self.newline();
1336 self.inc_indent();
1337 for e in elems {
1338 self.push_indent();
1339 self.format_expr(e);
1340 self.push_char(',');
1341 self.newline();
1342 }
1343 self.dec_indent();
1344 self.push_indent();
1345 }
1346 self.push_char(close);
1347 }
1348
1349 fn format_literal(&mut self, lit: &Literal) {
1350 match lit {
1351 Literal::Int(s) => self.push(s),
1352 Literal::Float(s) => self.push(s),
1353 Literal::Bool(b) => self.push(if *b { "true" } else { "false" }),
1354 Literal::Char(s) => {
1355 self.push_char('\'');
1356 self.push(s);
1357 self.push_char('\'');
1358 }
1359 Literal::String(s) => {
1360 self.push_char('"');
1361 self.push(&escape_string(s));
1362 self.push_char('"');
1363 }
1364 Literal::Unit => self.push("()"),
1365 }
1366 }
1367
1368 fn format_match_arm(&mut self, arm: &MatchArm) {
1369 self.emit_comments_before(arm.span.start);
1370 self.push_indent();
1371 self.format_pattern(&arm.pattern);
1372 if let Some(guard) = &arm.guard {
1373 self.push(" if ");
1374 self.format_expr(guard);
1375 }
1376 self.push(" => ");
1377 match &arm.body {
1378 Expr::Block { block, .. } => {
1379 self.format_block(block);
1380 self.push_char(',');
1381 }
1382 _ => {
1383 self.format_expr(&arm.body);
1384 self.push_char(',');
1385 }
1386 }
1387 self.newline();
1388 }
1389
1390 fn format_block(&mut self, block: &Block) {
1393 self.push_char('{');
1394 if block.stmts.is_empty() && block.tail.is_none() {
1395 self.push_char('}');
1396 return;
1397 }
1398 self.newline();
1399 self.inc_indent();
1400 self.format_block_body(block);
1401 self.dec_indent();
1402 self.push_indent();
1403 self.push_char('}');
1404 }
1405
1406 fn format_block_body(&mut self, block: &Block) {
1407 for stmt in &block.stmts {
1408 self.format_stmt(stmt);
1409 }
1410 if let Some(tail) = &block.tail {
1411 self.emit_comments_before(tail.span().start);
1412 self.push_indent();
1413 self.format_expr(tail);
1414 self.newline();
1415 }
1416 }
1417
1418 fn format_stmt(&mut self, stmt: &Stmt) {
1421 match stmt {
1422 Stmt::Let(let_stmt) => self.format_let_stmt(let_stmt),
1423 Stmt::Expr(expr) => {
1424 self.emit_comments_before(expr.span().start);
1425 self.push_indent();
1426 self.format_expr(expr);
1427 self.newline();
1428 }
1429 Stmt::For(for_loop) => self.format_for_loop(for_loop),
1430 Stmt::While(while_loop) => self.format_while_loop(while_loop),
1431 Stmt::Loop(loop_stmt) => self.format_loop_stmt(loop_stmt),
1432 Stmt::Guard(guard) => self.format_guard_stmt(guard),
1433 Stmt::Handling(handling) => self.format_handling_block(handling),
1434 Stmt::Empty => {}
1435 }
1436 }
1437
1438 fn format_let_stmt(&mut self, stmt: &LetStmt) {
1439 self.emit_comments_before(stmt.span.start);
1440 self.push_indent();
1441 self.push("let ");
1442 self.format_pattern(&stmt.pattern);
1443 if let Some(ty) = &stmt.ty {
1444 self.push(": ");
1445 self.format_type_expr(ty);
1446 }
1447 self.push(" = ");
1448 self.format_expr(&stmt.value);
1449 self.newline();
1450 }
1451
1452 fn format_for_loop(&mut self, stmt: &ForLoop) {
1453 self.emit_comments_before(stmt.span.start);
1454 self.push_indent();
1455 self.push("for ");
1456 self.format_pattern(&stmt.pattern);
1457 self.push(" in ");
1458 self.format_expr(&stmt.iterable);
1459 self.push(" ");
1460 self.format_block(&stmt.body);
1461 self.newline();
1462 }
1463
1464 fn format_while_loop(&mut self, stmt: &WhileLoop) {
1465 self.emit_comments_before(stmt.span.start);
1466 self.push_indent();
1467 self.push("while ");
1468 self.format_expr(&stmt.condition);
1469 self.push(" ");
1470 self.format_block(&stmt.body);
1471 self.newline();
1472 }
1473
1474 fn format_loop_stmt(&mut self, stmt: &LoopStmt) {
1475 self.emit_comments_before(stmt.span.start);
1476 self.push_indent();
1477 self.push("loop ");
1478 self.format_block(&stmt.body);
1479 self.newline();
1480 }
1481
1482 fn format_guard_stmt(&mut self, stmt: &GuardStmt) {
1483 self.emit_comments_before(stmt.span.start);
1484 self.push_indent();
1485 if let Some(pat) = &stmt.let_pattern {
1486 self.push("guard (let ");
1487 self.format_pattern(pat);
1488 self.push(" = ");
1489 self.format_expr(&stmt.condition);
1490 self.push(") else ");
1491 } else {
1492 self.push("guard ");
1493 self.format_expr(&stmt.condition);
1494 self.push(" else ");
1495 }
1496 self.format_block(&stmt.else_block);
1497 self.newline();
1498 }
1499
1500 fn format_handling_block(&mut self, stmt: &HandlingBlock) {
1501 self.emit_comments_before(stmt.span.start);
1502 self.push_indent();
1503 self.push("handling (");
1504 for (i, h) in stmt.handlers.iter().enumerate() {
1505 if i > 0 {
1506 self.push(", ");
1507 }
1508 self.format_type_path(&h.effect);
1509 self.push(" with ");
1510 self.format_expr(&h.handler);
1511 }
1512 self.push(") ");
1513 self.format_block(&stmt.body);
1514 self.newline();
1515 }
1516}
1517
1518fn binop_str(op: BinOp) -> &'static str {
1521 match op {
1522 BinOp::Add => "+",
1523 BinOp::Sub => "-",
1524 BinOp::Mul => "*",
1525 BinOp::Div => "/",
1526 BinOp::Rem => "%",
1527 BinOp::Pow => "**",
1528 BinOp::Eq => "==",
1529 BinOp::Ne => "!=",
1530 BinOp::Lt => "<",
1531 BinOp::Le => "<=",
1532 BinOp::Gt => ">",
1533 BinOp::Ge => ">=",
1534 BinOp::And => "&&",
1535 BinOp::Or => "||",
1536 BinOp::BitAnd => "&",
1537 BinOp::BitOr => "|",
1538 BinOp::BitXor => "^",
1539 BinOp::Compose => ">>",
1540 BinOp::Is => "is",
1541 }
1542}
1543
1544fn unaryop_str(op: UnaryOp) -> &'static str {
1545 match op {
1546 UnaryOp::Neg => "-",
1547 UnaryOp::Not => "!",
1548 UnaryOp::BitNot => "~",
1549 }
1550}
1551
1552fn assignop_str(op: AssignOp) -> &'static str {
1553 match op {
1554 AssignOp::Assign => "=",
1555 AssignOp::AddAssign => "+=",
1556 AssignOp::SubAssign => "-=",
1557 AssignOp::MulAssign => "*=",
1558 AssignOp::DivAssign => "/=",
1559 AssignOp::RemAssign => "%=",
1560 }
1561}
1562
1563fn import_category(import: &ImportDecl) -> u8 {
1565 let first = import
1566 .path
1567 .segments
1568 .first()
1569 .map(|s| s.name.as_str())
1570 .unwrap_or("");
1571 match first {
1572 "Core" => 0,
1573 "Std" => 1,
1574 _ => {
1575 if first.starts_with(|c: char| c.is_uppercase()) {
1577 2
1578 } else {
1579 3
1580 }
1581 }
1582 }
1583}
1584
1585fn import_path_str(import: &ImportDecl) -> String {
1586 import
1587 .path
1588 .segments
1589 .iter()
1590 .map(|s| s.name.as_str())
1591 .collect::<Vec<_>>()
1592 .join(".")
1593}
1594
1595fn wrap_long_lines(input: &str) -> String {
1601 let mut output = String::with_capacity(input.len());
1602 for line in input.lines() {
1603 if line.len() <= HARD_LIMIT {
1604 output.push_str(line);
1605 output.push('\n');
1606 } else {
1607 wrap_single_line(line, &mut output);
1608 }
1609 }
1610 output
1611}
1612
1613fn wrap_single_line(line: &str, output: &mut String) {
1615 let base_indent = line.len() - line.trim_start().len();
1616 let continuation_indent = base_indent + INDENT_WIDTH * 2;
1617 let cont_prefix: String = " ".repeat(continuation_indent);
1618
1619 let mut remaining = line.to_string();
1620 while remaining.len() > HARD_LIMIT {
1621 if let Some(pos) = find_break_point(&remaining, HARD_LIMIT) {
1622 let (before, after) = split_at_break(&remaining, pos);
1623 output.push_str(&before);
1624 output.push('\n');
1625 remaining = format!("{cont_prefix}{after}");
1626 } else {
1627 break;
1629 }
1630 }
1631 output.push_str(&remaining);
1632 output.push('\n');
1633}
1634
1635fn find_break_point(line: &str, limit: usize) -> Option<usize> {
1645 if line.len() <= limit {
1646 return None;
1647 }
1648
1649 let search_end = limit.min(line.len());
1652
1653 let mut best: Option<usize> = None;
1655
1656 let bytes = line.as_bytes();
1659
1660 for i in 0..search_end.saturating_sub(1) {
1662 if bytes[i] == b',' && i + 1 < search_end && bytes[i + 1] == b' ' {
1663 let candidate = i + 2; if candidate <= limit {
1665 best = Some(candidate);
1666 }
1667 }
1668 }
1669
1670 let op_patterns: &[&str] = &[
1674 " && ", " || ", " == ", " != ", " <= ", " >= ", " |> ", " >> ", " => ", " + ", " - ",
1675 " * ", " / ", " % ", " < ", " > ", " = ",
1676 ];
1677 for pat in op_patterns {
1678 let mut start = 0;
1680 while let Some(pos) = line[start..search_end].find(pat) {
1681 let abs_pos = start + pos;
1682 let candidate = abs_pos + 1; if candidate <= limit
1685 && candidate > 0
1686 && (best.is_none() || candidate > best.unwrap_or(0))
1687 {
1688 best = Some(candidate);
1689 }
1690 start = abs_pos + 1;
1691 }
1692 }
1693
1694 for i in 1..search_end {
1696 if bytes[i] == b'.' && (i + 1 >= line.len() || bytes[i + 1] != b'.') && bytes[i - 1] != b'.'
1697 {
1698 let candidate = i; if candidate <= limit
1700 && candidate > 0
1701 && (best.is_none() || candidate > best.unwrap_or(0))
1702 {
1703 best = Some(candidate);
1704 }
1705 }
1706 }
1707
1708 let content_start = line.len() - line.trim_start().len();
1710 best.filter(|&pos| pos > content_start)
1711}
1712
1713fn split_at_break(line: &str, pos: usize) -> (String, String) {
1716 let before = line[..pos].trim_end().to_string();
1717 let after = line[pos..].trim_start().to_string();
1718 (before, after)
1719}
1720
1721fn escape_string(s: &str) -> String {
1723 let mut out = String::with_capacity(s.len());
1724 for c in s.chars() {
1725 match c {
1726 '"' => out.push_str("\\\""),
1727 '\\' => out.push_str("\\\\"),
1728 '\n' => out.push_str("\\n"),
1729 '\r' => out.push_str("\\r"),
1730 '\t' => out.push_str("\\t"),
1731 _ => out.push(c),
1732 }
1733 }
1734 out
1735}