1use crate::{
2 ast::{
3 Annotation, ArgBy, ArgName, ArgVia, AssignmentKind, AssignmentPattern, BinOp,
4 ByteArrayFormatPreference, CAPTURE_VARIABLE, CallArg, CurveType, DataType, Decorator,
5 Definition, Function, LogicalOpChainKind, ModuleConstant, Namespace, OnTestFailure,
6 Pattern, RecordConstructor, RecordConstructorArg, RecordUpdateSpread, Span, TraceKind,
7 TypeAlias, TypedArg, TypedValidator, UnOp, UnqualifiedImport, UntypedArg, UntypedArgVia,
8 UntypedAssignmentKind, UntypedClause, UntypedDefinition, UntypedFunction, UntypedIfBranch,
9 UntypedModule, UntypedPattern, UntypedRecordUpdateArg, Use, Validator,
10 },
11 docvec,
12 expr::{DEFAULT_ERROR_STR, DEFAULT_TODO_STR, FnStyle, TypedExpr, UntypedExpr},
13 parser::{
14 extra::{Comment, ModuleExtra},
15 token::Base,
16 },
17 pretty::{Document, Documentable, break_, concat, flex_break, join, line, lines, nil},
18 tipo::{self, Type},
19};
20use itertools::Itertools;
21use num_bigint::BigInt;
22use ordinal::Ordinal;
23use std::rc::Rc;
24use vec1::Vec1;
25
26pub const INDENT: isize = 2;
27pub const MAX_COLUMNS: isize = 80;
28
29pub fn pretty(writer: &mut String, module: UntypedModule, extra: ModuleExtra, src: &str) {
30 let intermediate = Intermediate {
31 comments: extra
32 .comments
33 .iter()
34 .map(|span| Comment::from((span, src)))
35 .collect(),
36 doc_comments: extra
37 .doc_comments
38 .iter()
39 .map(|span| Comment::from((span, src)))
40 .collect(),
41 empty_lines: &extra.empty_lines,
42 module_comments: extra
43 .module_comments
44 .iter()
45 .map(|span| Comment::from((span, src)))
46 .collect(),
47 };
48
49 Formatter::with_comments(&intermediate)
50 .module(&module)
51 .pretty_print(MAX_COLUMNS, writer);
52}
53
54#[derive(Debug)]
55struct Intermediate<'a> {
56 comments: Vec<Comment<'a>>,
57 doc_comments: Vec<Comment<'a>>,
58 module_comments: Vec<Comment<'a>>,
59 empty_lines: &'a [usize],
60}
61
62#[derive(Debug, Clone, Default)]
64pub struct Formatter<'a> {
65 comments: &'a [Comment<'a>],
66 doc_comments: &'a [Comment<'a>],
67 module_comments: &'a [Comment<'a>],
68 empty_lines: &'a [usize],
69}
70
71impl<'comments> Formatter<'comments> {
72 pub fn new() -> Self {
73 Default::default()
74 }
75
76 fn with_comments(extra: &'comments Intermediate<'comments>) -> Self {
77 Self {
78 comments: &extra.comments,
79 doc_comments: &extra.doc_comments,
80 module_comments: &extra.module_comments,
81 empty_lines: extra.empty_lines,
82 }
83 }
84
85 fn pop_comments(
88 &mut self,
89 limit: usize,
90 ) -> impl Iterator<Item = Option<&'comments str>> + use<'comments> {
91 let (popped, rest, empty_lines) =
92 comments_before(self.comments, self.empty_lines, limit, true);
93
94 self.comments = rest;
95
96 self.empty_lines = empty_lines;
97
98 popped
99 }
100
101 fn pop_doc_comments(&mut self, limit: usize) -> impl Iterator<Item = Option<&'comments str>> {
104 let (popped, rest, empty_lines) =
105 comments_before(self.doc_comments, self.empty_lines, limit, false);
106
107 self.doc_comments = rest;
108
109 self.empty_lines = empty_lines;
110
111 popped
112 }
113
114 fn pop_empty_lines(&mut self, limit: usize) -> bool {
117 let mut end = 0;
118
119 for (i, &position) in self.empty_lines.iter().enumerate() {
120 if position > limit {
121 break;
122 }
123 end = i + 1;
124 }
125
126 self.empty_lines = self
127 .empty_lines
128 .get(end..)
129 .expect("Pop empty lines slicing");
130
131 end != 0
132 }
133
134 pub fn definitions<'a>(&mut self, definitions: &'a [UntypedDefinition]) -> Document<'a> {
135 let mut has_imports = false;
136 let mut has_declarations = false;
137 let mut imports = Vec::new();
138 let mut declarations = Vec::with_capacity(definitions.len());
139
140 for def in definitions {
141 let start = def.location().start;
142
143 match def {
144 Definition::Use(import) => {
145 has_imports = true;
146
147 let comments = self.pop_comments(start);
148
149 let def = self.definition(def);
150
151 imports.push((import, commented(def, comments)))
152 }
153
154 _other => {
155 has_declarations = true;
156
157 let comments = self.pop_comments(start);
158
159 let declaration = self.documented_definition(def);
160
161 declarations.push(commented(declaration, comments))
162 }
163 }
164 }
165
166 let imports = join(
167 imports
168 .into_iter()
169 .sorted_by(|(import_a, _), (import_b, _)| {
170 Ord::cmp(&import_a.module, &import_b.module)
171 })
172 .map(|(_, doc)| doc),
173 line(),
174 );
175
176 let declarations = join(declarations, lines(2));
177
178 let sep = if has_imports && has_declarations {
179 lines(2)
180 } else {
181 nil()
182 };
183
184 docvec![imports, sep, declarations]
185 }
186
187 fn module<'a>(&mut self, module: &'a UntypedModule) -> Document<'a> {
188 let defs = self.definitions(&module.definitions);
189
190 let doc_comments = join(
194 self.doc_comments.iter().map(|comment| {
195 "///"
196 .to_doc()
197 .append(Document::String(comment.content.to_string()))
198 }),
199 line(),
200 );
201
202 let comments = match printed_comments(self.pop_comments(usize::MAX), false) {
203 Some(comments) => comments,
204 None => nil(),
205 };
206
207 let module_comments = if !self.module_comments.is_empty() {
208 let comments = self.module_comments.iter().map(|s| {
209 "////"
210 .to_doc()
211 .append(Document::String(s.content.to_string()))
212 });
213
214 join(comments, line()).append(line())
215 } else {
216 nil()
217 };
218
219 let non_empty = vec![module_comments, defs, doc_comments, comments]
220 .into_iter()
221 .filter(|doc| !doc.is_empty());
222
223 join(non_empty, line()).append(line())
224 }
225
226 fn definition<'a>(&mut self, definition: &'a UntypedDefinition) -> Document<'a> {
227 match definition {
228 Definition::Fn(Function {
229 name,
230 arguments: args,
231 body,
232 public,
233 return_annotation,
234 end_position,
235 ..
236 }) => self.definition_fn(
237 public,
238 name,
239 args,
240 return_annotation,
241 body,
242 *end_position,
243 false,
244 ),
245
246 Definition::Validator(Validator {
247 end_position,
248 handlers,
249 fallback,
250 params,
251 name,
252 ..
253 }) => self.definition_validator(name, params, handlers, fallback, *end_position),
254
255 Definition::Test(Function {
256 name,
257 arguments: args,
258 body,
259 end_position,
260 on_test_failure,
261 ..
262 }) => self.definition_test(name, args, body, *end_position, on_test_failure),
263
264 Definition::Benchmark(Function {
265 name,
266 arguments: args,
267 body,
268 end_position,
269 on_test_failure,
270 ..
271 }) => self.definition_benchmark(name, args, body, *end_position, on_test_failure),
272
273 Definition::TypeAlias(TypeAlias {
274 alias,
275 parameters: args,
276 annotation: resolved_type,
277 public,
278 ..
279 }) => self.type_alias(*public, alias, args, resolved_type),
280
281 Definition::DataType(DataType {
282 name,
283 parameters,
284 public,
285 constructors,
286 location,
287 opaque,
288 decorators,
289 ..
290 }) => self.data_type(
291 *public,
292 *opaque,
293 name,
294 parameters,
295 constructors,
296 decorators,
297 location,
298 ),
299
300 Definition::Use(import) => self.import(import),
301
302 Definition::ModuleConstant(ModuleConstant {
303 public,
304 name,
305 annotation,
306 value,
307 ..
308 }) => {
309 let head = pub_(*public).append("const ").append(name.as_str());
310 let head = match annotation {
311 None => head,
312 Some(t) => head.append(": ").append(self.annotation(t)),
313 };
314
315 head.append(" =")
316 .append(break_("", " "))
317 .append(self.expr(value, false))
318 .nest(INDENT)
319 .group()
320 }
321 }
322 }
323
324 fn import<'a>(
325 &mut self,
326 Use {
327 module,
328 as_name,
329 unqualified: (_, unqualified),
330 ..
331 }: &'a Use<()>,
332 ) -> Document<'a> {
333 "use "
334 .to_doc()
335 .append(Document::String(module.join("/")))
336 .append(if unqualified.is_empty() {
337 nil()
338 } else {
339 let unqualified = join(
340 unqualified
341 .iter()
342 .sorted_by(|a, b| a.name.cmp(&b.name))
343 .map(|e| e.to_doc()),
344 flex_break(",", ", "),
345 );
346
347 break_(".{", ".{")
348 .append(unqualified)
349 .nest(INDENT)
350 .append(break_(",", ""))
351 .append("}")
352 .group()
353 })
354 .append(if let Some(name) = as_name {
355 docvec![" as ", name]
356 } else {
357 nil()
358 })
359 }
360
361 pub fn docs_const_expr<'a>(&mut self, name: &'a str, value: &'a TypedExpr) -> Document<'a> {
362 let mut printer = tipo::pretty::Printer::new();
363 let doc = name
364 .to_doc()
365 .append(": ")
366 .append(printer.print(&value.tipo()));
367
368 let value = self.const_expr(value);
370 if value.is_empty() {
371 doc
372 } else {
373 doc.append(" = ").append(value)
374 }
375 }
376
377 pub fn const_expr<'a>(&mut self, value: &'a TypedExpr) -> Document<'a> {
378 match value {
379 TypedExpr::UInt { value, base, .. } => self.int(value, base),
380 TypedExpr::String { value, .. } => self.string(value),
381 TypedExpr::ByteArray {
382 bytes,
383 preferred_format,
384 ..
385 } => self.bytearray(
386 &bytes
387 .iter()
388 .map(|b| (*b, Span::empty()))
389 .collect::<Vec<(u8, Span)>>(),
390 None,
391 preferred_format,
392 ),
393 TypedExpr::CurvePoint {
394 point,
395 preferred_format,
396 ..
397 } => self.bytearray(
398 &point
399 .compress()
400 .into_iter()
401 .map(|b| (b, Span::empty()))
402 .collect::<Vec<(u8, Span)>>(),
403 Some(point.as_ref().into()),
404 preferred_format,
405 ),
406 TypedExpr::Tuple { elems, .. } => {
407 wrap_args(elems.iter().map(|e| (self.const_expr(e), false))).group()
408 }
409 TypedExpr::Pair { fst, snd, .. } => {
410 let elems = [fst, snd];
411 "Pair"
412 .to_doc()
413 .append(wrap_args(elems.iter().map(|e| (self.const_expr(e), false))).group())
414 }
415 TypedExpr::List { elements, .. } => {
416 let comma: fn() -> Document<'a> =
417 if elements.iter().all(TypedExpr::is_simple_expr_to_format) {
418 || flex_break(",", ", ")
419 } else {
420 || break_(",", ", ")
421 };
422
423 list(
424 join(elements.iter().map(|e| self.const_expr(e)), comma()),
425 elements.len(),
426 None,
427 )
428 }
429 TypedExpr::Var { name, .. } => name.to_doc(),
430 TypedExpr::UnOp { value, op, .. } => match op {
431 UnOp::Not => docvec!["!", self.const_expr(value)],
432 UnOp::Negate => docvec!["-", self.const_expr(value)],
433 },
434 _ => Document::Str(""),
435 }
436 }
437
438 fn documented_definition<'a>(&mut self, s: &'a UntypedDefinition) -> Document<'a> {
439 let comments = self.doc_comments(s.location().start);
440 comments.append(self.definition(s).group()).group()
441 }
442
443 fn doc_comments<'a>(&mut self, limit: usize) -> Document<'a> {
444 let mut comments = self.pop_doc_comments(limit).peekable();
445 match comments.peek() {
446 None => nil(),
447 Some(_) => join(
448 comments.map(|c| match c {
449 Some(c) => "///".to_doc().append(Document::String(c.to_string())),
450 None => unreachable!("empty lines dropped by pop_doc_comments"),
451 }),
452 line(),
453 )
454 .append(line())
455 .force_break(),
456 }
457 }
458
459 fn type_annotation_constructor<'a>(
460 &mut self,
461 module: &'a Option<String>,
462 name: &'a str,
463 args: &'a [Annotation],
464 ) -> Document<'a> {
465 let head = module
466 .as_ref()
467 .map(|qualifier| qualifier.to_doc().append(".").append(name))
468 .unwrap_or_else(|| name.to_doc());
469
470 if args.is_empty() {
471 head
472 } else {
473 head.append(self.type_arguments(args))
474 }
475 }
476
477 fn annotation<'a>(&mut self, t: &'a Annotation) -> Document<'a> {
478 match t {
479 Annotation::Hole { name, .. } => name.to_doc(),
480
481 Annotation::Constructor {
482 name,
483 arguments: args,
484 module,
485 ..
486 } => self.type_annotation_constructor(module, name, args),
487
488 Annotation::Fn {
489 arguments: args,
490 ret: retrn,
491 ..
492 } => "fn"
493 .to_doc()
494 .append(wrap_args(args.iter().map(|t| {
495 let comments = self.pop_comments(t.location().start);
496
497 let doc_comments = self.doc_comments(t.location().start);
498
499 let doc = doc_comments.append(self.annotation(t)).group();
500
501 let doc = commented(doc, comments);
502
503 (doc, false)
504 })))
505 .group()
506 .append(" ->")
507 .append(break_("", " ").append(self.annotation(retrn)).nest(INDENT)),
508
509 Annotation::Var { name, .. } => name.to_doc(),
510 Annotation::Tuple { elems, .. } => {
511 wrap_args(elems.iter().map(|t| (self.annotation(t), false)))
512 }
513 Annotation::Pair { fst, snd, .. } => "Pair"
514 .to_doc()
515 .append("<")
516 .append(self.annotation(fst))
517 .append(break_(",", ", "))
518 .append(self.annotation(snd))
519 .append(">")
520 .group(),
521 }
522 .group()
523 }
524
525 pub fn type_arguments<'a>(&mut self, args: &'a [Annotation]) -> Document<'a> {
526 wrap_generics(args.iter().map(|t| self.annotation(t)))
527 }
528
529 pub fn type_alias<'a>(
530 &mut self,
531 public: bool,
532 name: &'a str,
533 args: &'a [String],
534 typ: &'a Annotation,
535 ) -> Document<'a> {
536 let head = pub_(public).append("type ").append(name);
537
538 let head = if args.is_empty() {
539 head
540 } else {
541 head.append(wrap_generics(args.iter().map(|e| e.to_doc())).group())
542 };
543
544 head.append(" =")
545 .append(line().append(self.annotation(typ)).group().nest(INDENT))
546 }
547
548 fn fn_arg<'a>(&mut self, arg: &'a UntypedArg) -> Document<'a> {
549 let comments = self.pop_comments(arg.location.start);
550
551 let doc_comments = self.doc_comments(arg.location.start);
552
553 let mut doc = match arg.by {
554 ArgBy::ByName(ref arg_name) => arg_name.to_doc(),
555 ArgBy::ByPattern(ref pattern) => self.pattern(pattern),
556 };
557
558 doc = match &arg.annotation {
559 None => doc,
560 Some(a) => doc.append(": ").append(self.annotation(a)),
561 }
562 .group();
563
564 let doc = doc_comments.append(doc.group()).group();
565
566 commented(doc, comments)
567 }
568
569 fn fn_arg_via<'a>(&mut self, arg_via: &'a ArgVia<UntypedArg, UntypedExpr>) -> Document<'a> {
570 let comments = self.pop_comments(arg_via.arg.location.start);
571
572 let doc_comments = self.doc_comments(arg_via.arg.location.start);
573
574 let mut doc = match arg_via.arg.by {
575 ArgBy::ByName(ref arg_name) => arg_name.to_doc(),
576 ArgBy::ByPattern(ref pattern) => self.pattern(pattern),
577 };
578
579 doc = match &arg_via.arg.annotation {
580 None => doc,
581 Some(a) => doc.append(": ").append(self.annotation(a)),
582 }
583 .append(" via ")
584 .append(self.expr(&arg_via.via, false))
585 .group();
586
587 let doc = doc_comments.append(doc.group()).group();
588
589 commented(doc, comments)
590 }
591
592 #[allow(clippy::too_many_arguments)]
593 fn definition_fn<'a>(
594 &mut self,
595 public: &'a bool,
596 name: &'a str,
597 args: &'a [UntypedArg],
598 return_annotation: &'a Option<Annotation>,
599 body: &'a UntypedExpr,
600 end_location: usize,
601 is_validator: bool,
602 ) -> Document<'a> {
603 let head = if !is_validator {
605 pub_(*public)
606 .append("fn ")
607 .append(name)
608 .append(wrap_args(args.iter().map(|e| (self.fn_arg(e), false))))
609 } else {
610 name.to_doc()
611 .append(wrap_args(args.iter().map(|e| (self.fn_arg(e), false))))
612 };
613
614 let head = match return_annotation {
616 Some(anno) => {
617 let is_bool = anno.is_logically_equal(&Annotation::boolean(Span::empty()));
618 if is_validator && is_bool {
619 head
620 } else {
621 head.append(" -> ").append(self.annotation(anno))
622 }
623 }
624 None => head,
625 }
626 .group();
627
628 let body = self.expr(body, true);
630
631 let body = match printed_comments(self.pop_comments(end_location), false) {
633 Some(comments) => body.append(line()).append(comments),
634 None => body,
635 };
636
637 head.append(" {")
639 .append(line().append(body).nest(INDENT).group())
640 .append(line())
641 .append("}")
642 }
643
644 #[allow(clippy::too_many_arguments)]
645 fn definition_test_or_bench<'a>(
646 &mut self,
647 keyword: &'static str,
648 name: &'a str,
649 args: &'a [UntypedArgVia],
650 body: &'a UntypedExpr,
651 end_location: usize,
652 on_test_failure: &'a OnTestFailure,
653 ) -> Document<'a> {
654 let head = keyword
656 .to_doc()
657 .append(" ")
658 .append(name)
659 .append(wrap_args(args.iter().map(|e| (self.fn_arg_via(e), false))))
660 .append(if keyword == "test" {
661 match on_test_failure {
662 OnTestFailure::FailImmediately => "",
663 OnTestFailure::SucceedEventually => " fail",
664 OnTestFailure::SucceedImmediately => " fail once",
665 }
666 } else {
667 ""
668 })
669 .group();
670
671 let body = self.expr(body, true);
673
674 let body = match printed_comments(self.pop_comments(end_location), false) {
676 Some(comments) => body.append(line()).append(comments),
677 None => body,
678 };
679
680 head.append(" {")
682 .append(line().append(body).nest(INDENT).group())
683 .append(line())
684 .append("}")
685 }
686
687 #[allow(clippy::too_many_arguments)]
688 fn definition_test<'a>(
689 &mut self,
690 name: &'a str,
691 args: &'a [UntypedArgVia],
692 body: &'a UntypedExpr,
693 end_location: usize,
694 on_test_failure: &'a OnTestFailure,
695 ) -> Document<'a> {
696 self.definition_test_or_bench("test", name, args, body, end_location, on_test_failure)
697 }
698
699 #[allow(clippy::too_many_arguments)]
700 fn definition_benchmark<'a>(
701 &mut self,
702 name: &'a str,
703 args: &'a [UntypedArgVia],
704 body: &'a UntypedExpr,
705 end_location: usize,
706 on_test_failure: &'a OnTestFailure,
707 ) -> Document<'a> {
708 self.definition_test_or_bench("bench", name, args, body, end_location, on_test_failure)
709 }
710
711 fn definition_validator<'a>(
712 &mut self,
713 name: &'a str,
714 params: &'a [UntypedArg],
715 handlers: &'a [UntypedFunction],
716 fallback: &'a UntypedFunction,
717 end_position: usize,
718 ) -> Document<'a> {
719 let v_head = "validator"
721 .to_doc()
722 .append(" ")
723 .append(name)
724 .append(if !params.is_empty() {
725 wrap_args(params.iter().map(|e| (self.fn_arg(e), false)))
726 } else {
727 nil()
728 });
729
730 let mut handler_docs = vec![];
731
732 for handler in handlers.iter() {
733 let fun_comments = self.pop_comments(handler.location.start);
734 let fun_doc_comments = self.doc_comments(handler.location.start);
735
736 let first_fn = self
737 .definition_fn(
738 &handler.public,
739 &handler.name,
740 &handler.arguments,
741 &handler.return_annotation,
742 &handler.body,
743 handler.end_position,
744 true,
745 )
746 .group();
747
748 let first_fn = commented(fun_doc_comments.append(first_fn).group(), fun_comments);
749
750 handler_docs.push(first_fn);
751 }
752
753 let is_exhaustive = handlers.len() >= TypedValidator::available_handler_names().len() - 1;
754
755 if !is_exhaustive || !fallback.is_default_fallback() {
756 let fallback_comments = self.pop_comments(fallback.location.start);
757 let fallback_doc_comments = self.doc_comments(fallback.location.start);
758
759 let fallback_fn = self
760 .definition_fn(
761 &fallback.public,
762 &fallback.name,
763 &fallback.arguments,
764 &fallback.return_annotation,
765 &fallback.body,
766 fallback.end_position,
767 true,
768 )
769 .group();
770
771 let fallback_fn = commented(
772 fallback_doc_comments.append(fallback_fn).group(),
773 fallback_comments,
774 );
775
776 handler_docs.push(fallback_fn);
777 }
778
779 let v_body = line().append(join(handler_docs, lines(2)));
780
781 let v_body = match printed_comments(self.pop_comments(end_position), false) {
782 Some(comments) => v_body.append(lines(2)).append(comments).nest(INDENT),
783 None => v_body.nest(INDENT),
784 };
785
786 v_head
787 .append(" {")
788 .append(v_body)
789 .append(line())
790 .append("}")
791 }
792
793 fn expr_fn<'a>(
794 &mut self,
795 args: &'a [UntypedArg],
796 return_annotation: Option<&'a Annotation>,
797 body: &'a UntypedExpr,
798 ) -> Document<'a> {
799 let args = wrap_args(args.iter().map(|e| (self.fn_arg(e), false))).group();
800 let body = match body {
801 UntypedExpr::Trace { .. }
802 | UntypedExpr::When { .. }
803 | UntypedExpr::LogicalOpChain { .. } => self.expr(body, true).force_break(),
804 _ => self.expr(body, true),
805 };
806
807 let header = "fn".to_doc().append(args);
808
809 let header = match return_annotation {
810 None => header,
811 Some(t) => header.append(" -> ").append(self.annotation(t)),
812 };
813
814 header
815 .append(
816 break_(" {", " { ")
817 .append(body)
818 .nest(INDENT)
819 .append(break_("", " "))
820 .append("}"),
821 )
822 .group()
823 }
824
825 fn sequence<'a>(&mut self, expressions: &'a [UntypedExpr]) -> Document<'a> {
826 let count = expressions.len();
827 let mut documents = Vec::with_capacity(count * 2);
828
829 for (i, expression) in expressions.iter().enumerate() {
830 let preceding_newline = self.pop_empty_lines(expression.start_byte_index());
831
832 if i != 0 && preceding_newline {
833 documents.push(lines(2));
834 } else if i != 0 {
835 documents.push(lines(1));
836 }
837
838 documents.push(self.expr(expression, false).group());
839 }
840
841 documents.to_doc().force_break()
842 }
843
844 fn assignment<'a>(
845 &mut self,
846 patterns: &'a Vec1<AssignmentPattern>,
847 value: &'a UntypedExpr,
848 kind: UntypedAssignmentKind,
849 comment: Option<&'_ str>,
850 ) -> Document<'a> {
851 let keyword = match kind {
852 AssignmentKind::Is => unreachable!(),
853 AssignmentKind::Let { .. } => "let",
854 AssignmentKind::Expect { .. } => "expect",
855 };
856
857 let symbol = if kind.is_backpassing() { "<-" } else { "=" };
858
859 let header = comment
860 .map(|comment| {
861 Document::String(format!("/// {comment}"))
862 .append(Document::Line(1))
863 .append(keyword.to_doc())
864 })
865 .unwrap_or_else(|| keyword.to_doc());
866
867 match patterns.first() {
868 AssignmentPattern {
869 pattern:
870 UntypedPattern::Constructor {
871 name, module: None, ..
872 },
873 annotation,
874 location: _,
875 } if name == "True"
876 && annotation.is_none()
877 && kind.is_expect()
878 && patterns.len() == 1 =>
879 {
880 header.append(self.case_clause_value(value))
881 }
882 _ => {
883 let patterns = patterns.into_iter().map(
884 |AssignmentPattern {
885 pattern,
886 annotation,
887 location: _,
888 }| {
889 self.pop_empty_lines(pattern.location().end);
890
891 let pattern = self.pattern(pattern);
892
893 let annotation = annotation
894 .as_ref()
895 .map(|a| ": ".to_doc().append(self.annotation(a)));
896
897 pattern.append(annotation).group()
898 },
899 );
900
901 header
902 .append(break_(" ", " "))
903 .append(join(patterns, break_(",", ", ")))
904 .group()
905 .nest(INDENT)
906 .append(break_("", " "))
907 .append(symbol)
908 .group()
909 .nest(INDENT)
910 .append(self.assignment_value(value))
911 }
912 }
913 }
914
915 pub fn bytearray<'a>(
916 &mut self,
917 bytes: &[(u8, Span)],
918 curve: Option<CurveType>,
919 preferred_format: &ByteArrayFormatPreference,
920 ) -> Document<'a> {
921 match preferred_format {
922 ByteArrayFormatPreference::HexadecimalString => "#"
923 .to_doc()
924 .append(Document::String(
925 curve.map(|c| c.to_string()).unwrap_or_default(),
926 ))
927 .append("\"")
928 .append(Document::String(hex::encode(
929 bytes.iter().map(|(b, _)| *b).collect::<Vec<u8>>(),
930 )))
931 .append("\""),
932 ByteArrayFormatPreference::ArrayOfBytes(Base::Decimal { .. }) => "#"
933 .to_doc()
934 .append(Document::String(
935 curve.map(|c| c.to_string()).unwrap_or_default(),
936 ))
937 .append(
938 break_("[", "[")
939 .append(join(
940 bytes.iter().map(|b| {
941 let doc = b.0.to_doc();
942
943 if b.1 == Span::empty() {
944 doc
945 } else {
946 commented(doc, self.pop_comments(b.1.start))
947 }
948 }),
949 break_(",", ", "),
950 ))
951 .nest(INDENT)
952 .append(break_(",", ""))
953 .append("]"),
954 )
955 .group(),
956 ByteArrayFormatPreference::ArrayOfBytes(Base::Hexadecimal) => "#"
957 .to_doc()
958 .append(Document::String(
959 curve.map(|c| c.to_string()).unwrap_or_default(),
960 ))
961 .append(
962 break_("[", "[")
963 .append(join(
964 bytes.iter().map(|b| {
965 let doc = Document::String(if b.0 < 16 {
966 format!("0x0{:x}", b.0)
967 } else {
968 format!("{:#x}", b.0)
969 });
970
971 if b.1 == Span::empty() {
972 doc
973 } else {
974 commented(doc, self.pop_comments(b.1.start))
975 }
976 }),
977 break_(",", ", "),
978 ))
979 .nest(INDENT)
980 .append(break_(",", ""))
981 .append("]"),
982 )
983 .group(),
984 ByteArrayFormatPreference::Utf8String => nil()
985 .append("\"")
986 .append(Document::String(escape(
987 core::str::from_utf8(&bytes.iter().map(|(b, _)| *b).collect::<Vec<u8>>())
988 .unwrap(),
989 )))
990 .append("\""),
991 }
992 }
993
994 pub fn int<'a>(&mut self, s: &'a str, base: &Base) -> Document<'a> {
995 match s.chars().next() {
996 Some('-') => Document::Str("-").append(self.uint(&s[1..], base)),
997 _ => self.uint(s, base),
998 }
999 }
1000
1001 pub fn uint<'a>(&mut self, s: &'a str, base: &Base) -> Document<'a> {
1002 match base {
1003 Base::Decimal { numeric_underscore } if *numeric_underscore => {
1004 let s = s
1005 .chars()
1006 .rev()
1007 .enumerate()
1008 .flat_map(|(i, c)| {
1009 if i != 0 && i % 3 == 0 {
1010 Some('_')
1011 } else {
1012 None
1013 }
1014 .into_iter()
1015 .chain(std::iter::once(c))
1016 })
1017 .collect::<String>()
1018 .chars()
1019 .rev()
1020 .collect::<String>();
1021
1022 Document::String(s)
1023 }
1024 Base::Decimal { .. } => s.to_doc(),
1025 Base::Hexadecimal => Document::String(format!(
1026 "0x{}",
1027 BigInt::parse_bytes(s.as_bytes(), 10)
1028 .expect("Invalid parsed hexadecimal digits ?!")
1029 .to_str_radix(16),
1030 )),
1031 }
1032 }
1033
1034 pub fn expr<'a>(&mut self, expr: &'a UntypedExpr, is_top_level: bool) -> Document<'a> {
1035 let comments = self.pop_comments(expr.start_byte_index());
1036
1037 let document = match expr {
1038 UntypedExpr::ByteArray {
1039 bytes,
1040 preferred_format,
1041 ..
1042 } => self.bytearray(bytes, None, preferred_format),
1043
1044 UntypedExpr::CurvePoint {
1045 point,
1046 preferred_format,
1047 ..
1048 } => self.bytearray(
1049 &point
1050 .compress()
1051 .into_iter()
1052 .map(|b| (b, Span::empty()))
1053 .collect::<Vec<(u8, Span)>>(),
1054 Some(point.as_ref().into()),
1055 preferred_format,
1056 ),
1057
1058 UntypedExpr::If {
1059 branches,
1060 final_else,
1061 ..
1062 } => self.if_expr(branches, final_else),
1063
1064 UntypedExpr::LogicalOpChain {
1065 kind, expressions, ..
1066 } => self.logical_op_chain(kind, expressions),
1067
1068 UntypedExpr::PipeLine {
1069 expressions,
1070 one_liner,
1071 } => self.pipeline(expressions, *one_liner),
1072
1073 UntypedExpr::UInt { value, base, .. } => self.uint(value, base),
1074
1075 UntypedExpr::String { value, .. } => self.string(value),
1076
1077 UntypedExpr::Sequence { expressions, .. } => {
1078 let sequence = self.sequence(expressions);
1079
1080 if is_top_level {
1081 sequence
1082 } else {
1083 "{".to_doc()
1084 .append(line().append(sequence).nest(INDENT).group())
1085 .append(line())
1086 .append("}")
1087 }
1088 }
1089
1090 UntypedExpr::Var { name, .. } if name.contains(CAPTURE_VARIABLE) => "_"
1091 .to_doc()
1092 .append(name.split('_').next_back().unwrap_or_default()),
1093
1094 UntypedExpr::Var { name, .. } => name.to_doc(),
1095
1096 UntypedExpr::UnOp { value, op, .. } => self.un_op(value, op),
1097
1098 UntypedExpr::Fn {
1099 fn_style: FnStyle::Capture,
1100 body,
1101 ..
1102 } => self.fn_capture(body),
1103
1104 UntypedExpr::Fn {
1105 fn_style: FnStyle::BinOp(op),
1106 ..
1107 } => op.to_doc(),
1108
1109 UntypedExpr::Fn {
1110 fn_style: FnStyle::Plain,
1111 return_annotation,
1112 arguments: args,
1113 body,
1114 ..
1115 } => self.expr_fn(args, return_annotation.as_ref(), body),
1116
1117 UntypedExpr::List { elements, tail, .. } => self.list(elements, tail.as_deref()),
1118
1119 UntypedExpr::Call {
1120 fun,
1121 arguments: args,
1122 ..
1123 } => self.call(fun, args),
1124
1125 UntypedExpr::BinOp {
1126 name, left, right, ..
1127 } => self.bin_op(name, left, right),
1128
1129 UntypedExpr::Assignment {
1130 value,
1131 patterns,
1132 kind,
1133 comment,
1134 ..
1135 } => self.assignment(patterns, value, *kind, comment.as_deref()),
1136
1137 UntypedExpr::Trace {
1138 kind,
1139 label,
1140 then,
1141 arguments,
1142 ..
1143 } => self.trace(kind, label, arguments, then),
1144
1145 UntypedExpr::When {
1146 subject, clauses, ..
1147 } => self.when(subject, clauses),
1148
1149 UntypedExpr::FieldAccess {
1150 label, container, ..
1151 } => self
1152 .expr(container, false)
1153 .append(".")
1154 .append(label.as_str()),
1155
1156 UntypedExpr::RecordUpdate {
1157 constructor,
1158 spread,
1159 arguments: args,
1160 ..
1161 } => self.record_update(constructor, spread, args),
1162
1163 UntypedExpr::Tuple { elems, .. } => {
1164 wrap_args(elems.iter().map(|e| (self.wrap_expr(e), false))).group()
1165 }
1166
1167 UntypedExpr::Pair { fst, snd, .. } => {
1168 let elems = [fst, snd];
1169 "Pair"
1170 .to_doc()
1171 .append(wrap_args(elems.iter().map(|e| (self.wrap_expr(e), false))).group())
1172 }
1173
1174 UntypedExpr::TupleIndex { index, tuple, .. } => {
1175 let suffix = Ordinal(*index + 1).suffix().to_doc();
1176
1177 let expr_doc = self.expr(tuple, false);
1178
1179 let maybe_wrapped_expr = if matches!(&**tuple, UntypedExpr::PipeLine { .. }) {
1180 wrap_args(vec![(expr_doc, false)]).group()
1181 } else {
1182 expr_doc
1183 };
1184
1185 maybe_wrapped_expr
1186 .append(".".to_doc())
1187 .append((index + 1).to_doc())
1188 .append(suffix)
1189 }
1190
1191 UntypedExpr::ErrorTerm { .. } => "fail".to_doc(),
1192
1193 UntypedExpr::TraceIfFalse { value, .. } => self.trace_if_false(value),
1194 };
1195
1196 commented(document, comments)
1197 }
1198
1199 fn string<'a>(&self, string: &'a str) -> Document<'a> {
1200 let doc = "@"
1201 .to_doc()
1202 .append(Document::String(escape(string)).surround("\"", "\""));
1203 if string.contains('\n') {
1204 doc.force_break()
1205 } else {
1206 doc
1207 }
1208 }
1209
1210 pub fn trace_if_false<'a>(&mut self, value: &'a UntypedExpr) -> Document<'a> {
1211 docvec![self.wrap_unary_op(value), "?"]
1212 }
1213
1214 pub fn trace<'a>(
1215 &mut self,
1216 kind: &'a TraceKind,
1217 label: &'a UntypedExpr,
1218 arguments: &'a [UntypedExpr],
1219 then: &'a UntypedExpr,
1220 ) -> Document<'a> {
1221 let (keyword, default_label) = match kind {
1222 TraceKind::Trace => ("trace", None),
1223 TraceKind::Error => ("fail", Some(DEFAULT_ERROR_STR.to_string())),
1224 TraceKind::Todo => ("todo", Some(DEFAULT_TODO_STR.to_string())),
1225 };
1226
1227 let mut body = match label {
1228 UntypedExpr::String { value, .. } if Some(value) == default_label.as_ref() => {
1229 keyword.to_doc()
1230 }
1231 _ => keyword
1232 .to_doc()
1233 .append(" ")
1234 .append(self.wrap_expr(label))
1235 .group(),
1236 };
1237
1238 for (ix, arg) in arguments.iter().enumerate() {
1239 body = body
1240 .append(if ix == 0 { ": " } else { ", " })
1241 .append(self.wrap_expr(arg))
1242 .group();
1243 }
1244
1245 match kind {
1246 TraceKind::Error | TraceKind::Todo => body,
1247 TraceKind::Trace => body
1248 .append(if self.pop_empty_lines(then.start_byte_index()) {
1249 lines(2)
1250 } else {
1251 line()
1252 })
1253 .append(self.expr(then, true)),
1254 }
1255 }
1256
1257 pub fn pattern_constructor<'a>(
1258 &mut self,
1259 name: &'a str,
1260 args: &'a [CallArg<UntypedPattern>],
1261 module: &'a Option<Namespace>,
1262 spread_location: Option<Span>,
1263 is_record: bool,
1264 ) -> Document<'a> {
1265 fn is_breakable(expr: &UntypedPattern) -> bool {
1266 match expr {
1267 Pattern::Tuple { .. } | Pattern::List { .. } => true,
1268 Pattern::Constructor {
1269 arguments: args, ..
1270 } => !args.is_empty(),
1271 _ => false,
1272 }
1273 }
1274
1275 let name = match module {
1276 Some(Namespace::Module(m)) | Some(Namespace::Type(None, m)) => {
1277 m.to_doc().append(".").append(name)
1278 }
1279 Some(Namespace::Type(Some(m), c)) => m
1280 .to_doc()
1281 .append(".")
1282 .append(c.as_str())
1283 .append(".")
1284 .append(name),
1285 None => name.to_doc(),
1286 };
1287
1288 if args.is_empty() && spread_location.is_some() {
1289 if is_record {
1290 name.append(" { .. }")
1291 } else {
1292 name.append("(..)")
1293 }
1294 } else if args.is_empty() {
1295 name
1296 } else if let Some(spread_location) = spread_location {
1297 let args = args
1298 .iter()
1299 .map(|a| self.pattern_call_arg(a))
1300 .collect::<Vec<_>>();
1301
1302 let wrapped_args = if is_record {
1303 self.wrap_fields_with_spread(args, spread_location)
1304 } else {
1305 self.wrap_args_with_spread(args, spread_location)
1306 };
1307
1308 name.append(wrapped_args)
1309 } else {
1310 match args {
1311 [arg] if is_breakable(&arg.value) => name
1312 .append(if is_record { "{" } else { "(" })
1313 .append(self.pattern_call_arg(arg))
1314 .append(if is_record { "}" } else { ")" })
1315 .group(),
1316
1317 _ => name
1318 .append(wrap_args(
1319 args.iter().map(|a| (self.pattern_call_arg(a), is_record)),
1320 ))
1321 .group(),
1322 }
1323 }
1324 }
1325
1326 pub fn wrap_fields_with_spread<'a, I>(&mut self, args: I, spread_location: Span) -> Document<'a>
1327 where
1328 I: IntoIterator<Item = Document<'a>>,
1329 {
1330 let mut args = args.into_iter().peekable();
1331 if args.peek().is_none() {
1332 return "()".to_doc();
1333 }
1334
1335 let comments = self.pop_comments(spread_location.start);
1336
1337 break_(" {", " { ")
1338 .append(join(args, break_(",", ", ")))
1339 .append(break_(",", ", "))
1340 .append(commented("..".to_doc(), comments))
1341 .nest(INDENT)
1342 .append(break_("", " "))
1343 .append("}")
1344 .group()
1345 }
1346
1347 pub fn wrap_args_with_spread<'a, I>(&mut self, args: I, spread_location: Span) -> Document<'a>
1348 where
1349 I: IntoIterator<Item = Document<'a>>,
1350 {
1351 let mut args = args.into_iter().peekable();
1352 if args.peek().is_none() {
1353 return "()".to_doc();
1354 }
1355
1356 let comments = self.pop_comments(spread_location.start);
1357
1358 break_("(", "(")
1359 .append(join(args, break_(",", ", ")))
1360 .append(break_(",", ", "))
1361 .append(commented("..".to_doc(), comments))
1362 .nest(INDENT)
1363 .append(break_(",", ""))
1364 .append(")")
1365 .group()
1366 }
1367
1368 fn call<'a>(&mut self, fun: &'a UntypedExpr, args: &'a [CallArg<UntypedExpr>]) -> Document<'a> {
1369 let is_constr = match fun {
1370 UntypedExpr::Var { name, .. } => name[0..1].chars().all(|c| c.is_uppercase()),
1371 UntypedExpr::FieldAccess { label, .. } => label[0..1].chars().all(|c| c.is_uppercase()),
1372 _ => false,
1373 };
1374
1375 let needs_curly = if is_constr {
1376 args.iter().all(|arg| arg.label.is_some())
1377 } else {
1378 false
1379 };
1380
1381 self.expr(fun, false)
1382 .append(wrap_args(
1383 args.iter()
1384 .map(|a| (self.call_arg(a, needs_curly), needs_curly)),
1385 ))
1386 .group()
1387 }
1388
1389 pub fn if_expr<'a>(
1390 &mut self,
1391 branches: &'a Vec1<UntypedIfBranch>,
1392 final_else: &'a UntypedExpr,
1393 ) -> Document<'a> {
1394 let if_branches = self
1395 .if_branch(Document::Str("if "), branches.first())
1396 .append(join(
1397 branches[1..].iter().map(|branch| {
1398 self.if_branch(line().append(break_("} else if", "} else if ")), branch)
1399 }),
1400 nil(),
1401 ));
1402
1403 let else_begin = line().append("} else {");
1404
1405 let else_body = line().append(self.expr(final_else, true)).nest(INDENT);
1406
1407 let else_end = line().append("}");
1408
1409 if_branches
1410 .append(else_begin)
1411 .append(else_body)
1412 .append(else_end)
1413 .force_break()
1414 }
1415
1416 pub fn if_branch<'a>(
1417 &mut self,
1418 if_keyword: Document<'a>,
1419 branch: &'a UntypedIfBranch,
1420 ) -> Document<'a> {
1421 let if_begin = if_keyword
1422 .append(self.wrap_expr(&branch.condition))
1423 .append(match &branch.is {
1424 Some(AssignmentPattern {
1425 pattern,
1426 annotation,
1427 ..
1428 }) => {
1429 let is_sugar = matches!(
1430 (&pattern, &branch.condition),
1431 (
1432 Pattern::Var { name, .. },
1433 UntypedExpr::Var { name: var_name, .. }
1434 ) if name == var_name
1435 );
1436
1437 let Some(annotation) = &annotation else {
1438 unreachable!()
1439 };
1440
1441 let is = if is_sugar {
1442 self.annotation(annotation)
1443 } else {
1444 self.pattern(pattern)
1445 .append(": ")
1446 .append(self.annotation(annotation))
1447 .group()
1448 };
1449
1450 break_("", " ").append("is ").append(is)
1451 }
1452 None => nil(),
1453 })
1454 .append(Document::Str(" {"))
1455 .group();
1456
1457 let if_body = line().append(self.expr(&branch.body, true)).nest(INDENT);
1458
1459 if_begin.append(if_body)
1460 }
1461
1462 pub fn when<'a>(
1463 &mut self,
1464 subject: &'a UntypedExpr,
1465 clauses: &'a [UntypedClause],
1466 ) -> Document<'a> {
1467 let subjects_doc = break_("when", "when ")
1468 .append(self.wrap_expr(subject))
1469 .nest(INDENT)
1470 .append(break_("", " "))
1471 .append("is {")
1472 .group();
1473
1474 let clauses_doc = concat(
1475 clauses
1476 .iter()
1477 .enumerate()
1478 .map(|(i, c)| self.clause(c, i as u32)),
1479 );
1480
1481 subjects_doc
1482 .append(line().append(clauses_doc).nest(INDENT))
1483 .append(line())
1484 .append("}")
1485 .force_break()
1486 }
1487
1488 pub fn record_update<'a>(
1489 &mut self,
1490 constructor: &'a UntypedExpr,
1491 spread: &'a RecordUpdateSpread,
1492 args: &'a [UntypedRecordUpdateArg],
1493 ) -> Document<'a> {
1494 use std::iter::once;
1495 let constructor_doc = self.expr(constructor, false);
1496 let spread_doc = "..".to_doc().append(self.expr(&spread.base, false));
1497 let arg_docs = args.iter().map(|a| (self.record_update_arg(a), true));
1498 let all_arg_docs = once((spread_doc, true)).chain(arg_docs);
1499 constructor_doc.append(wrap_args(all_arg_docs)).group()
1500 }
1501
1502 pub fn bin_op<'a>(
1503 &mut self,
1504 name: &'a BinOp,
1505 left: &'a UntypedExpr,
1506 right: &'a UntypedExpr,
1507 ) -> Document<'a> {
1508 let precedence = name.precedence();
1509
1510 let left_precedence = left.binop_precedence();
1511 let right_precedence = right.binop_precedence();
1512
1513 let mut left = self.expr(left, false);
1514 if left.fits(MAX_COLUMNS) {
1515 left = left.force_unbroken()
1516 }
1517
1518 let mut right = self.expr(right, false);
1519 if right.fits(MAX_COLUMNS) {
1520 right = right.force_unbroken()
1521 }
1522
1523 self.operator_side(
1524 left,
1525 precedence,
1526 if matches!(name, BinOp::Or | BinOp::And) {
1527 left_precedence.saturating_sub(1)
1528 } else {
1529 left_precedence
1530 },
1531 )
1532 .append(" ")
1533 .append(name)
1534 .append(" ")
1535 .append(self.operator_side(
1536 right,
1537 precedence,
1538 if matches!(name, BinOp::Or | BinOp::And) {
1539 right_precedence
1540 } else {
1541 right_precedence.saturating_sub(1)
1542 },
1543 ))
1544 }
1545
1546 pub fn operator_side<'a>(&mut self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> {
1547 if op > side {
1548 break_("(", "( ")
1549 .append(doc)
1550 .nest(INDENT)
1551 .append(break_("", " "))
1552 .append(")")
1553 .group()
1554 } else {
1555 doc
1556 }
1557 }
1558
1559 fn logical_op_chain<'a>(
1560 &mut self,
1561 kind: &'a LogicalOpChainKind,
1562 expressions: &'a [UntypedExpr],
1563 ) -> Document<'a> {
1564 kind.to_doc()
1565 .append(" {")
1566 .append(
1567 line()
1568 .append(join(
1569 expressions
1570 .iter()
1571 .map(|expression| self.expr(expression, false)),
1572 ",".to_doc().append(line()),
1573 ))
1574 .nest(INDENT)
1575 .group(),
1576 )
1577 .append(",")
1578 .append(line())
1579 .append("}")
1580 }
1581
1582 fn pipeline<'a>(
1583 &mut self,
1584 expressions: &'a Vec1<UntypedExpr>,
1585 one_liner: bool,
1586 ) -> Document<'a> {
1587 let mut docs = Vec::with_capacity(expressions.len() * 3);
1588
1589 let first = expressions.first();
1590
1591 let first_precedence = first.binop_precedence();
1592
1593 let first = self.wrap_expr(first);
1594
1595 docs.push(self.operator_side(first, 5, first_precedence));
1596
1597 for expr in expressions.iter().skip(1) {
1598 let comments = self.pop_comments(expr.location().start);
1599
1600 let doc = match expr {
1601 UntypedExpr::Fn {
1602 fn_style: FnStyle::Capture,
1603 body,
1604 ..
1605 } => self.pipe_capture_right_hand_side(body),
1606
1607 _ => self.wrap_expr(expr),
1608 };
1609
1610 let space = if one_liner { break_("", " ") } else { line() };
1611
1612 let pipe = space
1613 .append(commented("|> ".to_doc(), comments))
1614 .nest(INDENT);
1615
1616 docs.push(pipe);
1617
1618 let expr = self
1619 .operator_side(doc, 4, expr.binop_precedence())
1620 .nest(2 * INDENT);
1621
1622 docs.push(expr);
1623 }
1624
1625 if one_liner {
1626 docs.to_doc().group()
1627 } else {
1628 docs.to_doc().force_break()
1629 }
1630 }
1631
1632 fn pipe_capture_right_hand_side<'a>(&mut self, fun: &'a UntypedExpr) -> Document<'a> {
1633 let (fun, args) = match fun {
1634 UntypedExpr::Call {
1635 fun,
1636 arguments: args,
1637 ..
1638 } => (fun, args),
1639 _ => panic!("Function capture found not to have a function call body when formatting"),
1640 };
1641
1642 let hole_in_first_position = matches!(
1643 args.first(),
1644 Some(CallArg {
1645 value: UntypedExpr::Var { name, .. },
1646 ..
1647 }) if name.contains(CAPTURE_VARIABLE)
1648 );
1649
1650 if hole_in_first_position && args.len() == 1 {
1651 self.expr(fun, false)
1653 } else if hole_in_first_position {
1654 self.expr(fun, false).append(
1656 wrap_args(
1657 args.iter()
1658 .skip(1)
1659 .map(|a| (self.call_arg(a, false), false)),
1660 )
1661 .group(),
1662 )
1663 } else {
1664 self.expr(fun, false)
1666 .append(wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group())
1667 }
1668 }
1669
1670 fn fn_capture<'a>(&mut self, call: &'a UntypedExpr) -> Document<'a> {
1671 match call {
1672 UntypedExpr::Call {
1673 fun,
1674 arguments: args,
1675 ..
1676 } => match args.as_slice() {
1677 [first, second] if is_breakable_expr(&second.value) && first.is_capture_hole() => {
1678 let discard_name = match first.value {
1679 UntypedExpr::Var { ref name, .. } => name.split("_").last().unwrap_or("_"),
1680 _ => "",
1681 };
1682 self.expr(fun, false)
1683 .append("(_")
1684 .append(discard_name)
1685 .append(", ")
1686 .append(self.call_arg(second, false))
1687 .append(")")
1688 .group()
1689 }
1690
1691 _ => self.expr(fun, false).append(
1692 wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group(),
1693 ),
1694 },
1695
1696 _ => panic!("Function capture body found not to be a call in the formatter",),
1698 }
1699 }
1700
1701 pub fn record_constructor<'a, A>(
1702 &mut self,
1703 constructor: &'a RecordConstructor<A>,
1704 ) -> Document<'a> {
1705 let comments = self.pop_comments(constructor.location.start);
1706 let doc_comments = self.doc_comments(constructor.location.start);
1707
1708 let doc = if constructor.arguments.is_empty() {
1709 self.decorator(&constructor.decorators)
1710 .append(if constructor.decorators.is_empty() {
1711 nil()
1712 } else {
1713 line()
1714 })
1715 .append(constructor.name.as_str())
1716 } else if constructor.sugar {
1717 wrap_fields(constructor.arguments.iter().map(
1718 |RecordConstructorArg {
1719 label,
1720 annotation,
1721 location,
1722 ..
1723 }| {
1724 let arg_comments = self.pop_comments(location.start);
1725
1726 let arg = match label {
1727 Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)),
1728 None => self.annotation(annotation),
1729 };
1730
1731 commented(
1732 self.doc_comments(location.start).append(arg).group(),
1733 arg_comments,
1734 )
1735 },
1736 ))
1737 .group()
1738 } else {
1739 self.decorator(&constructor.decorators)
1740 .append(if constructor.decorators.is_empty() {
1741 nil()
1742 } else {
1743 line()
1744 })
1745 .append(constructor.name.as_str())
1746 .append(wrap_args(constructor.arguments.iter().map(
1747 |RecordConstructorArg {
1748 label,
1749 annotation,
1750 location,
1751 ..
1752 }| {
1753 let arg_comments = self.pop_comments(location.start);
1754
1755 let arg = match label {
1756 Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)),
1757 None => self.annotation(annotation),
1758 };
1759
1760 (
1761 commented(
1762 self.doc_comments(location.start).append(arg).group(),
1763 arg_comments,
1764 ),
1765 label.is_some(),
1766 )
1767 },
1768 )))
1769 .group()
1770 };
1771
1772 commented(doc_comments.append(doc).group(), comments)
1773 }
1774
1775 #[allow(clippy::too_many_arguments)]
1776 pub fn data_type<'a, A>(
1777 &mut self,
1778 public: bool,
1779 opaque: bool,
1780 name: &'a str,
1781 args: &'a [String],
1782 constructors: &'a [RecordConstructor<A>],
1783 decorators: &'a [Decorator],
1784 location: &'a Span,
1785 ) -> Document<'a> {
1786 self.pop_empty_lines(location.start);
1787
1788 let mut is_sugar = false;
1789
1790 self.decorator(decorators)
1791 .append(if decorators.is_empty() { nil() } else { line() })
1792 .append(pub_(public))
1793 .append(if opaque { "opaque type " } else { "type " })
1794 .append(if args.is_empty() {
1795 name.to_doc()
1796 } else {
1797 name.to_doc()
1798 .append(wrap_generics(args.iter().map(|e| e.to_doc())))
1799 .group()
1800 })
1801 .append(" {")
1802 .append(if constructors.len() == 1 && constructors[0].sugar {
1803 is_sugar = true;
1804
1805 self.record_constructor(&constructors[0])
1806 } else {
1807 concat(constructors.iter().map(|c| {
1808 if self.pop_empty_lines(c.location.start) {
1809 lines(2)
1810 } else {
1811 line()
1812 }
1813 .append(self.record_constructor(c))
1814 .nest(INDENT)
1815 .group()
1816 }))
1817 })
1818 .append(if is_sugar { nil() } else { line() })
1819 .append("}")
1820 }
1821
1822 pub fn decorator<'a>(&mut self, decorators: &'a [Decorator]) -> Document<'a> {
1823 join(
1824 decorators.iter().map(|d| match &d.kind {
1825 crate::ast::DecoratorKind::Tag { value, base } => {
1826 docvec![
1827 "@tag(",
1828 Document::String(match base {
1829 Base::Decimal { .. } => value.to_string(),
1830 Base::Hexadecimal => format!("{value:#x}"),
1831 }),
1832 ")"
1833 ]
1834 }
1835 crate::ast::DecoratorKind::List => "@list".to_doc(),
1836 }),
1837 line(),
1838 )
1839 }
1840
1841 pub fn docs_data_type<'a, A>(
1842 &mut self,
1843 name: &'a str,
1844 args: &'a [String],
1845 constructors: &'a [RecordConstructor<A>],
1846 location: &'a Span,
1847 ) -> Document<'a> {
1848 self.pop_empty_lines(location.start);
1849
1850 let mut is_sugar = false;
1851
1852 (if args.is_empty() {
1853 name.to_doc()
1854 } else {
1855 name.to_doc()
1856 .append(wrap_generics(args.iter().map(|e| e.to_doc())))
1857 .group()
1858 })
1859 .append(" {")
1860 .append(if constructors.len() == 1 && constructors[0].sugar {
1861 is_sugar = true;
1862
1863 self.record_constructor(&constructors[0])
1864 } else {
1865 concat(constructors.iter().map(|c| {
1866 if self.pop_empty_lines(c.location.start) {
1867 lines(2)
1868 } else {
1869 line()
1870 }
1871 .append(self.record_constructor(c))
1872 .nest(INDENT)
1873 .group()
1874 }))
1875 })
1876 .append(if is_sugar { nil() } else { line() })
1877 .append("}")
1878 }
1879
1880 pub fn docs_opaque_data_type<'a>(
1881 &mut self,
1882 name: &'a str,
1883 args: &'a [String],
1884 location: &'a Span,
1885 ) -> Document<'a> {
1886 self.pop_empty_lines(location.start);
1887 if args.is_empty() {
1888 name.to_doc()
1889 } else {
1890 name.to_doc()
1891 .append(wrap_generics(args.iter().map(|e| e.to_doc())).group())
1892 }
1893 }
1894
1895 pub fn docs_type_alias<'a>(
1896 &mut self,
1897 name: &'a str,
1898 args: &'a [String],
1899 typ: &'a Annotation,
1900 ) -> Document<'a> {
1901 let head = name.to_doc();
1902
1903 let head = if args.is_empty() {
1904 head
1905 } else {
1906 head.append(wrap_generics(args.iter().map(|e| e.to_doc())).group())
1907 };
1908
1909 head.append(" = ")
1910 .append(self.annotation(typ).group().nest(INDENT))
1911 }
1912
1913 pub fn docs_record_constructor<'a, A>(
1914 &mut self,
1915 constructor: &'a RecordConstructor<A>,
1916 ) -> Document<'a> {
1917 if constructor.arguments.is_empty() {
1918 constructor.name.to_doc()
1919 } else {
1920 constructor
1921 .name
1922 .to_doc()
1923 .append(wrap_args(constructor.arguments.iter().map(|arg| {
1924 (
1925 (match &arg.label {
1926 Some(l) => l.to_doc().append(": "),
1927 None => "".to_doc(),
1928 })
1929 .append(self.annotation(&arg.annotation)),
1930 arg.label.is_some(),
1931 )
1932 })))
1933 .group()
1934 }
1935 }
1936
1937 pub fn docs_fn_signature<'a>(
1938 &mut self,
1939 name: &'a str,
1940 args: &'a [TypedArg],
1941 return_annotation: &'a Option<Annotation>,
1942 return_type: Rc<Type>,
1943 ) -> Document<'a> {
1944 let head = name.to_doc().append(self.docs_fn_args(args)).append(" -> ");
1945
1946 let tail = self.type_or_annotation(return_annotation, &return_type);
1947
1948 let doc = head.append(tail.clone()).group();
1949
1950 if doc.clone().to_pretty_string(MAX_COLUMNS).contains('\n') {
1952 let head = name
1953 .to_doc()
1954 .append(self.docs_fn_args(args).force_break())
1955 .append(" -> ");
1956 head.append(tail).group()
1957 } else {
1958 doc
1959 }
1960 }
1961
1962 pub fn docs_fn_args<'a>(&mut self, args: &'a [TypedArg]) -> Document<'a> {
1964 wrap_args(args.iter().map(|e| (self.docs_fn_arg(e), false)))
1965 }
1966
1967 fn docs_fn_arg<'a>(&mut self, arg: &'a TypedArg) -> Document<'a> {
1968 self.docs_fn_arg_name(&arg.arg_name)
1969 .append(self.type_or_annotation(&arg.annotation, &arg.tipo))
1970 .group()
1971 }
1972
1973 fn docs_fn_arg_name<'a>(&mut self, arg_name: &'a ArgName) -> Document<'a> {
1974 match arg_name {
1975 ArgName::Discarded { .. } => "".to_doc(),
1976 ArgName::Named { label, .. } => label.to_doc().append(": "),
1977 }
1978 }
1979
1980 fn type_or_annotation<'a>(
1982 &mut self,
1983 annotation: &'a Option<Annotation>,
1984 type_info: &Rc<Type>,
1985 ) -> Document<'a> {
1986 match annotation {
1987 Some(a) => self.annotation(a),
1988 None => tipo::pretty::Printer::new().print(type_info),
1989 }
1990 }
1991
1992 fn wrap_expr<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
1993 match expr {
1994 UntypedExpr::Trace {
1995 kind: TraceKind::Trace,
1996 ..
1997 }
1998 | UntypedExpr::Sequence { .. }
1999 | UntypedExpr::Assignment { .. } => "{"
2000 .to_doc()
2001 .append(line().append(self.expr(expr, true)).nest(INDENT))
2002 .append(line())
2003 .append("}")
2004 .force_break(),
2005
2006 _ => self.expr(expr, false),
2007 }
2008 }
2009
2010 fn call_arg<'a>(&mut self, arg: &'a CallArg<UntypedExpr>, can_pun: bool) -> Document<'a> {
2011 match &arg.label {
2012 Some(s) => {
2013 if can_pun && matches!(&arg.value, UntypedExpr::Var { name, .. } if name == s) {
2014 nil()
2015 } else {
2016 commented(
2017 s.to_doc().append(": "),
2018 self.pop_comments(arg.location.start),
2019 )
2020 }
2021 }
2022 None => nil(),
2023 }
2024 .append(self.wrap_expr(&arg.value))
2025 }
2026
2027 fn record_update_arg<'a>(&mut self, arg: &'a UntypedRecordUpdateArg) -> Document<'a> {
2028 if matches!(&arg.value, UntypedExpr::Var { name, .. } if name == &arg.label) {
2029 nil()
2030 } else {
2031 commented(
2032 arg.label.to_doc().append(": "),
2033 self.pop_comments(arg.location.start),
2034 )
2035 }
2036 .append(self.wrap_expr(&arg.value))
2037 }
2038
2039 fn case_clause_value<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
2040 match expr {
2041 UntypedExpr::Trace {
2042 kind: TraceKind::Trace,
2043 ..
2044 }
2045 | UntypedExpr::Sequence { .. }
2046 | UntypedExpr::Assignment { .. } => Document::Str(" {")
2047 .append(break_("", " ").nest(INDENT))
2048 .append(
2049 self.expr(expr, true)
2050 .nest(INDENT)
2051 .group()
2052 .append(line())
2053 .append("}")
2054 .force_break(),
2055 ),
2056
2057 UntypedExpr::Fn { .. } => line().append(self.expr(expr, false)).nest(INDENT).group(),
2058
2059 UntypedExpr::When { .. } => line().append(self.expr(expr, false)).nest(INDENT).group(),
2060
2061 _ => break_("", " ")
2062 .append(self.expr(expr, false))
2063 .nest(INDENT)
2064 .group(),
2065 }
2066 }
2067
2068 fn assignment_value<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
2069 match expr {
2070 UntypedExpr::Trace {
2071 kind: TraceKind::Trace,
2072 ..
2073 }
2074 | UntypedExpr::Sequence { .. }
2075 | UntypedExpr::Assignment { .. } => Document::Str(" {")
2076 .append(break_("", " ").nest(INDENT))
2077 .append(
2078 self.expr(expr, true)
2079 .nest(INDENT)
2080 .group()
2081 .append(line())
2082 .append("}")
2083 .force_break(),
2084 ),
2085
2086 _ => Document::Str(" ").append(self.expr(expr, false)).group(),
2087 }
2088 }
2089
2090 fn clause<'a>(&mut self, clause: &'a UntypedClause, index: u32) -> Document<'a> {
2091 let space_before = self.pop_empty_lines(clause.location.start);
2092 let clause_doc = join(
2093 clause.patterns.iter().map(|p| self.pattern(p)),
2094 break_(" |", " | "),
2095 )
2096 .group();
2097
2098 if index == 0 {
2099 clause_doc
2100 } else if space_before {
2101 lines(2).append(clause_doc)
2102 } else {
2103 lines(1).append(clause_doc)
2104 }
2105 .append(" ->")
2106 .append(self.case_clause_value(&clause.then))
2107 }
2108
2109 fn list<'a>(
2110 &mut self,
2111 elements: &'a [UntypedExpr],
2112 tail: Option<&'a UntypedExpr>,
2113 ) -> Document<'a> {
2114 let comma: fn() -> Document<'a> =
2115 if elements.iter().all(UntypedExpr::is_simple_expr_to_format) {
2116 || flex_break(",", ", ")
2117 } else {
2118 || break_(",", ", ")
2119 };
2120 let elements_document = join(elements.iter().map(|e| self.wrap_expr(e)), comma());
2121 let tail = tail.map(|e| self.expr(e, false));
2122 list(elements_document, elements.len(), tail)
2123 }
2124
2125 pub fn pattern<'a>(&mut self, pattern: &'a UntypedPattern) -> Document<'a> {
2126 let comments = self.pop_comments(pattern.location().start);
2127 let doc = match pattern {
2128 Pattern::Int { value, base, .. } => self.int(value, base),
2129
2130 Pattern::ByteArray {
2131 value,
2132 preferred_format,
2133 ..
2134 } => self.bytearray(value, None, preferred_format),
2135
2136 Pattern::Var { name, .. } => name.to_doc(),
2137
2138 Pattern::Assign { name, pattern, .. } => {
2139 self.pattern(pattern).append(" as ").append(name.as_str())
2140 }
2141
2142 Pattern::Discard { name, .. } => name.to_doc(),
2143
2144 Pattern::Tuple { elems, .. } => {
2145 wrap_args(elems.iter().map(|e| (self.pattern(e), false))).group()
2146 }
2147
2148 Pattern::Pair { fst, snd, .. } => "Pair"
2149 .to_doc()
2150 .append("(")
2151 .append(self.pattern(fst))
2152 .append(break_(",", ", "))
2153 .append(self.pattern(snd))
2154 .append(")")
2155 .group(),
2156
2157 Pattern::List { elements, tail, .. } => {
2158 let break_style: fn() -> Document<'a> =
2159 if elements.iter().all(Pattern::is_simple_pattern_to_format) {
2160 || flex_break(",", ", ")
2161 } else {
2162 || break_(",", ", ")
2163 };
2164
2165 let elements_document =
2166 join(elements.iter().map(|e| self.pattern(e)), break_style());
2167 let tail = tail.as_ref().map(|e| {
2168 if e.is_discard() {
2169 nil()
2170 } else {
2171 self.pattern(e)
2172 }
2173 });
2174 list(elements_document, elements.len(), tail)
2175 }
2176
2177 Pattern::Constructor {
2178 name,
2179 arguments: args,
2180 module,
2181 spread_location,
2182 is_record,
2183 ..
2184 } => self.pattern_constructor(name, args, module, *spread_location, *is_record),
2185 };
2186 commented(doc, comments)
2187 }
2188
2189 fn pattern_call_arg<'a>(&mut self, arg: &'a CallArg<UntypedPattern>) -> Document<'a> {
2190 let comments = self.pop_comments(arg.location.start);
2191
2192 if let (UntypedPattern::Var { name, .. }, Some(label)) = (&arg.value, &arg.label)
2193 && name == label
2194 {
2195 return self.pattern(&arg.value);
2196 }
2197
2198 let doc = arg
2199 .label
2200 .as_ref()
2201 .map(|s| s.to_doc().append(": "))
2202 .unwrap_or_else(nil)
2203 .append(self.pattern(&arg.value));
2204
2205 commented(doc, comments)
2206 }
2207
2208 fn un_op<'a>(&mut self, value: &'a UntypedExpr, op: &'a UnOp) -> Document<'a> {
2209 match op {
2210 UnOp::Not => docvec!["!", self.wrap_unary_op(value)],
2211 UnOp::Negate => docvec!["-", self.wrap_unary_op(value)],
2212 }
2213 }
2214
2215 fn wrap_unary_op<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
2216 match expr {
2217 UntypedExpr::Trace {
2218 kind: TraceKind::Error,
2219 ..
2220 }
2221 | UntypedExpr::Trace {
2222 kind: TraceKind::Todo,
2223 ..
2224 }
2225 | UntypedExpr::PipeLine { .. }
2226 | UntypedExpr::BinOp { .. }
2227 | UntypedExpr::UnOp { .. } => "(".to_doc().append(self.expr(expr, false)).append(")"),
2228 _ => self.wrap_expr(expr),
2229 }
2230 }
2231}
2232
2233impl<'a> Documentable<'a> for &'a ArgName {
2234 fn to_doc(self) -> Document<'a> {
2235 match self {
2236 ArgName::Discarded { label, name, .. } | ArgName::Named { label, name, .. } => {
2237 if label == name {
2238 name.to_doc()
2239 } else {
2240 docvec![label, " ", name]
2241 }
2242 }
2243 }
2244 }
2245}
2246
2247fn pub_(public: bool) -> Document<'static> {
2248 if public { "pub ".to_doc() } else { nil() }
2249}
2250
2251impl<'a> Documentable<'a> for &'a UnqualifiedImport {
2252 fn to_doc(self) -> Document<'a> {
2253 self.name.to_doc().append(match &self.as_name {
2254 None => nil(),
2255 Some(s) => " as ".to_doc().append(s.as_str()),
2256 })
2257 }
2258}
2259
2260impl<'a> Documentable<'a> for &'a LogicalOpChainKind {
2261 fn to_doc(self) -> Document<'a> {
2262 match self {
2263 LogicalOpChainKind::And => "and",
2264 LogicalOpChainKind::Or => "or",
2265 }
2266 .to_doc()
2267 }
2268}
2269
2270impl<'a> Documentable<'a> for &'a BinOp {
2271 fn to_doc(self) -> Document<'a> {
2272 match self {
2273 BinOp::And => "&&",
2274 BinOp::Or => "||",
2275 BinOp::LtInt => "<",
2276 BinOp::LtEqInt => "<=",
2277 BinOp::Eq => "==",
2278 BinOp::NotEq => "!=",
2279 BinOp::GtEqInt => ">=",
2280 BinOp::GtInt => ">",
2281 BinOp::AddInt => "+",
2282 BinOp::SubInt => "-",
2283 BinOp::MultInt => "*",
2284 BinOp::DivInt => "/",
2285 BinOp::ModInt => "%",
2286 }
2287 .to_doc()
2288 }
2289}
2290
2291pub fn wrap_args<'a, I>(args: I) -> Document<'a>
2292where
2293 I: IntoIterator<Item = (Document<'a>, bool)>,
2294{
2295 let mut args = args.into_iter().peekable();
2296
2297 let curly = if let Some((_, uses_curly)) = args.peek() {
2298 *uses_curly
2299 } else {
2300 return "()".to_doc();
2301 };
2302
2303 let args = args.map(|a| a.0);
2304
2305 let (open_broken, open_unbroken, close) = if curly {
2306 (" {", " { ", "}")
2307 } else {
2308 ("(", "(", ")")
2309 };
2310
2311 break_(open_broken, open_unbroken)
2312 .append(join(args, break_(",", ", ")))
2313 .nest(INDENT)
2314 .append(break_(",", if curly { " " } else { "" }))
2315 .append(close)
2316}
2317
2318pub fn wrap_generics<'a, I>(args: I) -> Document<'a>
2319where
2320 I: IntoIterator<Item = Document<'a>>,
2321{
2322 break_("<", "<")
2323 .append(join(args, break_(",", ", ")))
2324 .nest(INDENT)
2325 .append(break_(",", ""))
2326 .append(">")
2327}
2328
2329pub fn wrap_fields<'a, I>(args: I) -> Document<'a>
2330where
2331 I: IntoIterator<Item = Document<'a>>,
2332{
2333 let mut args = args.into_iter().peekable();
2334 if args.peek().is_none() {
2335 return nil();
2336 }
2337
2338 line()
2339 .append(join(args, ",".to_doc().append(line())))
2340 .nest(INDENT)
2341 .append(",")
2342 .append(line())
2343}
2344
2345fn list<'a>(elements: Document<'a>, length: usize, tail: Option<Document<'a>>) -> Document<'a> {
2346 if length == 0 {
2347 return match tail {
2348 Some(tail) => tail,
2349 None => "[]".to_doc(),
2350 };
2351 }
2352
2353 let doc = break_("[", "[").append(elements);
2354
2355 match tail {
2356 None => doc.nest(INDENT).append(break_(",", "")),
2357
2358 Some(Document::String(t)) if t == *"_" => doc
2360 .append(break_(",", ", "))
2361 .append("..")
2362 .nest(INDENT)
2363 .append(break_("", "")),
2364
2365 Some(final_tail) => doc
2366 .append(break_(",", ", "))
2367 .append("..")
2368 .append(final_tail)
2369 .nest(INDENT)
2370 .append(break_("", "")),
2371 }
2372 .append("]")
2373 .group()
2374}
2375
2376fn printed_comments<'a, 'comments>(
2377 comments: impl IntoIterator<Item = Option<&'comments str>>,
2378 trailing_newline: bool,
2379) -> Option<Document<'a>> {
2380 let mut comments = comments.into_iter().peekable();
2381 comments.peek()?;
2382
2383 let mut doc = Vec::new();
2384 while let Some(c) = comments.next() {
2385 match c {
2386 None => continue,
2387 Some(c) => {
2388 doc.push("//".to_doc().append(Document::String(c.to_string())));
2391 match comments.peek() {
2392 Some(Some(_)) => doc.push(line()),
2394 Some(None) => {
2396 comments.next();
2397 match comments.peek() {
2398 Some(_) => doc.push(lines(2)),
2399 None => {
2400 if trailing_newline {
2401 doc.push(lines(2));
2402 }
2403 }
2404 }
2405 }
2406 None => {
2408 if trailing_newline {
2409 doc.push(line());
2410 }
2411 }
2412 }
2413 }
2414 }
2415 }
2416 let doc = concat(doc);
2417 if trailing_newline {
2418 Some(doc.force_break())
2419 } else {
2420 Some(doc)
2421 }
2422}
2423
2424fn commented<'a, 'comments>(
2425 doc: Document<'a>,
2426 comments: impl IntoIterator<Item = Option<&'comments str>>,
2427) -> Document<'a> {
2428 match printed_comments(comments, true) {
2429 Some(comments) => comments.append(doc.group()),
2430 None => doc,
2431 }
2432}
2433
2434pub fn comments_before<'a>(
2435 comments: &'a [Comment<'a>],
2436 empty_lines: &'a [usize],
2437 limit: usize,
2438 retain_empty_lines: bool,
2439) -> (
2440 impl Iterator<Item = Option<&'a str>>,
2441 &'a [Comment<'a>],
2442 &'a [usize],
2443) {
2444 let end_comments = comments
2445 .iter()
2446 .position(|c| c.start > limit)
2447 .unwrap_or(comments.len());
2448 let end_empty_lines = empty_lines
2449 .iter()
2450 .position(|l| *l > limit)
2451 .unwrap_or(empty_lines.len());
2452 let popped_comments = comments
2453 .get(0..end_comments)
2454 .expect("0..end_comments is guaranteed to be in bounds")
2455 .iter()
2456 .map(|c| (c.start, Some(c.content)));
2457 let popped_empty_lines = if retain_empty_lines { empty_lines } else { &[] }
2458 .get(0..end_empty_lines)
2459 .unwrap_or(&[])
2460 .iter()
2461 .map(|i| (i, i))
2462 .coalesce(|(a_start, a_end), (b_start, b_end)| {
2464 if *a_end + 1 == *b_start {
2465 Ok((a_start, b_end))
2466 } else {
2467 Err(((a_start, a_end), (b_start, b_end)))
2468 }
2469 })
2470 .map(|l| (*l.0, None));
2471 let popped = popped_comments
2472 .merge_by(popped_empty_lines, |(a, _), (b, _)| a < b)
2473 .skip_while(|(_, comment_or_line)| comment_or_line.is_none())
2474 .map(|(_, comment_or_line)| comment_or_line);
2475 (
2476 popped,
2477 comments.get(end_comments..).expect("in bounds"),
2478 empty_lines.get(end_empty_lines..).expect("in bounds"),
2479 )
2480}
2481
2482fn is_breakable_expr(expr: &UntypedExpr) -> bool {
2483 matches!(
2484 expr,
2485 UntypedExpr::Fn { .. }
2486 | UntypedExpr::Sequence { .. }
2487 | UntypedExpr::Assignment { .. }
2488 | UntypedExpr::Call { .. }
2489 | UntypedExpr::When { .. }
2490 | UntypedExpr::List { .. }
2491 | UntypedExpr::If { .. }
2492 )
2493}
2494
2495fn escape(string: &str) -> String {
2496 string
2497 .chars()
2498 .flat_map(|c| match c {
2499 '\n' => vec!['\\', 'n'],
2500 '\r' => vec!['\\', 'r'],
2501 '\t' => vec!['\\', 't'],
2502 '\0' => vec!['\\', '0'],
2503 '"' => vec!['\\', c],
2504 '\\' => vec!['\\', c],
2505 _ => vec![c],
2506 })
2507 .collect::<String>()
2508}