Skip to main content

aiken_lang/
format.rs

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/// Hayleigh's bane
63#[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    // Pop comments that occur before a byte-index in the source, consuming
86    // and retaining any empty lines contained within.
87    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    // Pop doc comments that occur before a byte-index in the source, consuming
102    // and dropping any empty lines contained within.
103    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    // Remove between 0 and `limit` empty lines following the current position,
115    // returning true if any empty lines were removed.
116    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        // Now that `defs` has been collected, only freestanding comments (//)
191        // and doc comments (///) remain. Freestanding comments aren't associated
192        // with any statement, and are moved to the bottom of the module.
193        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        // NOTE: Only display the full value for simple expressions.
369        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        // Fn name and args
604        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        // Add return annotation
615        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        // Format body
629        let body = self.expr(body, true);
630
631        // Add any trailing comments
632        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        // Stick it all together
638        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        // Fn name and args
655        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        // Format body
672        let body = self.expr(body, true);
673
674        // Add any trailing comments
675        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        // Stick it all together
681        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        // validator name(params)
720        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            // x |> fun(_)
1652            self.expr(fun, false)
1653        } else if hole_in_first_position {
1654            // x |> fun(_, 2, 3)
1655            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            // x |> fun(1, _, 3)
1665            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            // The body of a capture being not a fn shouldn't be possible...
1697            _ => 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        // Wrap arguments on multi-lines if they are lengthy.
1951        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    // Will always print the types, even if they were implicit in the original source
1963    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    // Display type-annotation when available, or fallback to inferred type.
1981    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        // Don't print tail if it is a discard
2359        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                // There will never be consecutive empty lines (None values),
2389                // and whenever we peek a None, we advance past it.
2390                doc.push("//".to_doc().append(Document::String(c.to_string())));
2391                match comments.peek() {
2392                    // Next line is a comment
2393                    Some(Some(_)) => doc.push(line()),
2394                    // Next line is empty
2395                    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                    // We've reached the end, there are no more lines
2407                    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        // compact consecutive empty lines into a single line
2463        .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}