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