Skip to main content

cove_syntax/
format.rs

1//! Deterministic formatting of Cove source.
2//!
3//! The formatter prints an [`ast::SourceUnit`](crate::ast::SourceUnit) back to source text. It is
4//! deterministic and idempotent: formatting twice produces exactly what
5//! formatting once produced, and re-parsing the result produces the same tree.
6//!
7//! # Layout
8//!
9//! Two spaces of indentation, no tabs, no trailing whitespace, one trailing
10//! newline. Lines are kept within [`MAX_WIDTH`] columns where a legal break
11//! exists.
12//!
13//! # The newline rule
14//!
15//! Cove statements end at the end of a line, so a formatter cannot break a
16//! line wherever it likes: a break in the wrong place silently splits one
17//! statement into two. Every break this module introduces is one the parser
18//! reads as a continuation — inside a `(`, `[`, or `<` group, immediately
19//! after a binary operator, or before a leading `.` — which is why long
20//! argument lists break one argument per line, a binary expression breaks
21//! *after* its operator, and a method chain breaks *before* its dots.
22//!
23//! # Comments
24//!
25//! The tree carries `///` doc comments but not `//` or `/* */` comments, and
26//! not the blank lines an author wrote between statements. [`format_source`]
27//! reads those back out of the source text and re-attaches them by position:
28//! a comment on its own line attaches to the item or statement that follows
29//! it, and a comment at the end of a line stays at the end of that line. No
30//! comment is ever dropped; one the formatter cannot place exactly is moved
31//! to the nearest following boundary rather than lost.
32
33use cove_diag::Span;
34
35use crate::ast::{
36    Arg, BinaryOp, Block, EnumCase, EnumDecl, Expr, ExprKind, Field, FnDecl, GenericParam,
37    ImplBlock, Item, ItemKind, MatchArm, Param, Pattern, PatternKind, Receiver, SourceUnit, Stmt,
38    StmtKind, StrPart, StructDecl, TraitDecl, TraitMethod, Type, TypeAlias, TypeKind, UnaryOp, Use,
39};
40
41/// The column the formatter keeps lines within when a legal break exists.
42pub const MAX_WIDTH: usize = 80;
43
44/// One indentation step, in spaces.
45const INDENT: usize = 2;
46
47/// Formats one parsed source unit deterministically.
48///
49/// The tree does not carry `//` and `/* */` comments or the blank lines
50/// between statements, so this function cannot reproduce them. Use
51/// [`format_source`] to format a unit together with the text it was parsed
52/// from, which is what `cove fmt` does.
53pub fn format_unit(unit: &SourceUnit) -> String {
54    format_source("", unit)
55}
56
57/// Renders one expression on a single line, from the tree alone.
58///
59/// A literal's spelling is not in the tree, so `0xff` renders as `255`. That
60/// is enough for the places this is used: showing a parameter's default in a
61/// signature, where what matters is which value it is.
62pub fn format_expr(expr: &Expr) -> String {
63    Formatter::new("").flat(expr, 0)
64}
65
66/// Formats `unit`, which must be the tree parsed from `source`.
67///
68/// Reading the source alongside the tree is what lets the formatter keep
69/// comments, blank lines, and the exact spelling of numeric and string
70/// literals.
71pub fn format_source(source: &str, unit: &SourceUnit) -> String {
72    let mut formatter = Formatter::new(source);
73    formatter.source_unit(unit);
74    formatter.finish()
75}
76
77/// The display width of `text`, in characters.
78fn width(text: &str) -> usize {
79    text.chars().count()
80}
81
82// ---------------------------------------------------------------------------
83// Comments
84// ---------------------------------------------------------------------------
85
86/// One `//` or `/* */` comment found in the source text.
87///
88/// `///` doc comments are not collected: the parser already attached them to
89/// the declaration they document, so the tree prints them itself.
90#[derive(Clone, Debug)]
91struct Comment {
92    start: usize,
93    end: usize,
94    /// True when only whitespace precedes the comment on its line, which is
95    /// what makes it belong to the construct that follows rather than to the
96    /// line it sits on.
97    own_line: bool,
98    /// The comment text, with trailing whitespace removed from every line.
99    text: String,
100    /// The column the comment starts at, used to re-indent the continuation
101    /// lines of a multi-line block comment.
102    column: usize,
103}
104
105/// Every `//` and `/* */` comment in `source`, in source order.
106fn scan_comments(source: &str) -> Vec<Comment> {
107    let bytes = source.as_bytes();
108    let mut comments = Vec::new();
109    let mut i = 0;
110    while i < bytes.len() {
111        match bytes[i] {
112            b'"' => i = skip_string(bytes, i),
113            b'/' if bytes.get(i + 1) == Some(&b'/') => {
114                let is_doc = bytes.get(i + 2) == Some(&b'/') && bytes.get(i + 3) != Some(&b'/');
115                let end = line_end(bytes, i);
116                if !is_doc {
117                    comments.push(comment_at(source, i, end));
118                }
119                i = end;
120            }
121            b'/' if bytes.get(i + 1) == Some(&b'*') => {
122                let end = skip_block_comment(bytes, i);
123                comments.push(comment_at(source, i, end));
124                i = end;
125            }
126            _ => i += 1,
127        }
128    }
129    comments
130}
131
132fn comment_at(source: &str, start: usize, end: usize) -> Comment {
133    let before = &source[..start];
134    let line = before.rsplit('\n').next().unwrap_or("");
135    let text = source[start..end]
136        .lines()
137        .map(str::trim_end)
138        .collect::<Vec<_>>()
139        .join("\n");
140    Comment {
141        start,
142        end,
143        own_line: line.trim().is_empty(),
144        text,
145        column: width(line),
146    }
147}
148
149fn line_end(bytes: &[u8], from: usize) -> usize {
150    let mut i = from;
151    while i < bytes.len() && bytes[i] != b'\n' {
152        i += 1;
153    }
154    i
155}
156
157/// Skips a string literal, including the interpolations that may nest further
158/// strings inside it, exactly as the lexer does.
159fn skip_string(bytes: &[u8], start: usize) -> usize {
160    let mut i = start + 1;
161    while i < bytes.len() {
162        match bytes[i] {
163            b'\\' => i += 2,
164            b'"' => return i + 1,
165            b'{' => i = skip_interpolation(bytes, i + 1),
166            _ => i += 1,
167        }
168    }
169    i
170}
171
172fn skip_interpolation(bytes: &[u8], from: usize) -> usize {
173    let mut i = from;
174    while i < bytes.len() {
175        match bytes[i] {
176            b'}' => return i + 1,
177            b'{' => i = skip_interpolation(bytes, i + 1),
178            b'"' => i = skip_string(bytes, i),
179            _ => i += 1,
180        }
181    }
182    i
183}
184
185/// Skips a `/* ... */` comment. Block comments nest.
186fn skip_block_comment(bytes: &[u8], start: usize) -> usize {
187    let mut i = start + 2;
188    let mut depth = 1u32;
189    while i < bytes.len() {
190        if bytes[i] == b'*' && bytes.get(i + 1) == Some(&b'/') {
191            i += 2;
192            depth -= 1;
193            if depth == 0 {
194                return i;
195            }
196        } else if bytes[i] == b'/' && bytes.get(i + 1) == Some(&b'*') {
197            i += 2;
198            depth += 1;
199        } else {
200            i += 1;
201        }
202    }
203    i
204}
205
206// ---------------------------------------------------------------------------
207// Output
208// ---------------------------------------------------------------------------
209
210/// One output line: its code, and the comment that trails it, if any.
211#[derive(Default)]
212struct Line {
213    text: String,
214    comment: Option<String>,
215}
216
217/// The line buffer the formatter writes into.
218///
219/// Trailing comments are held beside their line rather than appended to it,
220/// so that a run of consecutive lines that all end in a comment can be
221/// aligned once the whole run is known.
222#[derive(Default)]
223struct Out {
224    lines: Vec<Line>,
225    current: String,
226    comment: Option<String>,
227    open: bool,
228    pending_blank: bool,
229}
230
231impl Out {
232    /// Ends the current line and begins a new one indented by `indent`.
233    ///
234    /// Calling this twice without writing anything in between re-indents the
235    /// line instead of emitting an empty one, so callers may start a line
236    /// without knowing whether their caller already did.
237    fn start_line(&mut self, indent: usize) {
238        let empty = self.current.trim().is_empty() && self.comment.is_none();
239        if self.open && !empty {
240            self.lines.push(Line {
241                text: std::mem::take(&mut self.current),
242                comment: self.comment.take(),
243            });
244        }
245        if self.pending_blank && !self.lines.is_empty() {
246            self.lines.push(Line::default());
247        }
248        self.pending_blank = false;
249        self.current = " ".repeat(indent);
250        self.open = true;
251    }
252
253    fn write(&mut self, text: &str) {
254        self.current.push_str(text);
255    }
256
257    /// The column the next character would be written at.
258    fn col(&self) -> usize {
259        width(&self.current)
260    }
261
262    /// Attaches `text` to the end of the current line.
263    ///
264    /// A comment with nothing before it on the line is written as ordinary
265    /// text instead, so that it never turns into a line of padding.
266    fn set_comment(&mut self, text: &str) {
267        if self.current.trim().is_empty() && self.comment.is_none() {
268            self.write(text);
269            return;
270        }
271        match &mut self.comment {
272            Some(existing) => {
273                existing.push(' ');
274                existing.push_str(text);
275            }
276            None => self.comment = Some(text.to_string()),
277        }
278    }
279
280    /// Renders the buffered lines, aligning each run of consecutive trailing
281    /// comments and ending the file with exactly one newline.
282    fn finish(mut self) -> String {
283        if self.open {
284            self.lines.push(Line {
285                text: self.current,
286                comment: self.comment,
287            });
288        }
289        while self
290            .lines
291            .last()
292            .is_some_and(|line| line.text.trim().is_empty() && line.comment.is_none())
293        {
294            self.lines.pop();
295        }
296
297        let mut out = String::new();
298        let mut i = 0;
299        while i < self.lines.len() {
300            if self.lines[i].comment.is_none() {
301                out.push_str(self.lines[i].text.trim_end());
302                out.push('\n');
303                i += 1;
304                continue;
305            }
306            let mut end = i;
307            let mut column = 0;
308            while end < self.lines.len() && self.lines[end].comment.is_some() {
309                column = column.max(width(self.lines[end].text.trim_end()));
310                end += 1;
311            }
312            for line in &self.lines[i..end] {
313                let text = line.text.trim_end();
314                out.push_str(text);
315                out.push_str(&" ".repeat(column - width(text) + 1));
316                out.push_str(line.comment.as_deref().unwrap_or(""));
317                out.push('\n');
318            }
319            i = end;
320        }
321        out
322    }
323}
324
325// ---------------------------------------------------------------------------
326// Precedence
327// ---------------------------------------------------------------------------
328
329/// Precedence levels, lowest first, matching the parser's descent.
330mod prec {
331    pub const RETURN: u8 = 0;
332    pub const ASSIGN: u8 = 1;
333    pub const OR: u8 = 2;
334    pub const AND: u8 = 3;
335    pub const COMPARISON: u8 = 4;
336    pub const RANGE: u8 = 5;
337    pub const ADDITIVE: u8 = 6;
338    pub const MULTIPLICATIVE: u8 = 7;
339    pub const UNARY: u8 = 8;
340    pub const POSTFIX: u8 = 9;
341    pub const PRIMARY: u8 = 10;
342}
343
344fn binary_prec(op: BinaryOp) -> u8 {
345    match op {
346        BinaryOp::Or => prec::OR,
347        BinaryOp::And => prec::AND,
348        BinaryOp::Eq
349        | BinaryOp::Ne
350        | BinaryOp::Lt
351        | BinaryOp::Le
352        | BinaryOp::Gt
353        | BinaryOp::Ge
354        | BinaryOp::Is => prec::COMPARISON,
355        BinaryOp::Add | BinaryOp::Sub => prec::ADDITIVE,
356        BinaryOp::Mul | BinaryOp::Div | BinaryOp::Rem => prec::MULTIPLICATIVE,
357    }
358}
359
360fn expr_prec(expr: &Expr) -> u8 {
361    match &expr.kind {
362        ExprKind::Return(_) | ExprKind::Break(_) => prec::RETURN,
363        ExprKind::Assign { .. } => prec::ASSIGN,
364        ExprKind::Binary { op, .. } => binary_prec(*op),
365        ExprKind::Range { .. } => prec::RANGE,
366        ExprKind::Unary { .. } | ExprKind::Await(_) => prec::UNARY,
367        ExprKind::Field { .. } | ExprKind::Call { .. } | ExprKind::Try(_) => prec::POSTFIX,
368        _ => prec::PRIMARY,
369    }
370}
371
372fn binary_symbol(op: BinaryOp) -> &'static str {
373    match op {
374        BinaryOp::Add => "+",
375        BinaryOp::Sub => "-",
376        BinaryOp::Mul => "*",
377        BinaryOp::Div => "/",
378        BinaryOp::Rem => "%",
379        BinaryOp::Eq => "==",
380        BinaryOp::Ne => "!=",
381        BinaryOp::Lt => "<",
382        BinaryOp::Le => "<=",
383        BinaryOp::Gt => ">",
384        BinaryOp::Ge => ">=",
385        BinaryOp::Is => "is",
386        BinaryOp::And => "&&",
387        BinaryOp::Or => "||",
388    }
389}
390
391fn unary_symbol(op: UnaryOp) -> &'static str {
392    match op {
393        UnaryOp::Not => "!",
394        UnaryOp::Neg => "-",
395    }
396}
397
398/// Whether the rendering of `expr` ends in a `}`.
399///
400/// The header of `if`, `while`, `for`, and `match` is parsed with trailing
401/// closures disabled, so a header that would end in a brace must be
402/// parenthesised or the body's `{` would be read as part of the header.
403fn ends_with_brace(expr: &Expr) -> bool {
404    match &expr.kind {
405        ExprKind::Block(_)
406        | ExprKind::If { .. }
407        | ExprKind::Match { .. }
408        | ExprKind::For { .. }
409        | ExprKind::While { .. }
410        | ExprKind::Scope { .. }
411        | ExprKind::Lambda { .. } => true,
412        ExprKind::Call { trailing, .. } => trailing.is_some(),
413        ExprKind::Binary { rhs, .. } => ends_with_brace(rhs),
414        ExprKind::Assign { value, .. } => ends_with_brace(value),
415        ExprKind::Range { end, .. } => ends_with_brace(end),
416        ExprKind::Unary { operand, .. } => ends_with_brace(operand),
417        ExprKind::Await(operand) => ends_with_brace(operand),
418        ExprKind::Return(Some(value)) | ExprKind::Break(Some(value)) => ends_with_brace(value),
419        _ => false,
420    }
421}
422
423/// The offset of the `}`, `]`, or `)` that a construct ending at `end`
424/// closes with.
425fn close_brace(end: u32) -> usize {
426    (end as usize).saturating_sub(1)
427}
428
429fn block_is_empty(block: &Block) -> bool {
430    block.statements.is_empty() && block.tail.is_none()
431}
432
433/// The body of a trailing closure, which the parser stores as a parameterless
434/// lambda.
435fn trailing_body(expr: &Expr) -> Option<&Block> {
436    match &expr.kind {
437        ExprKind::Lambda { body, .. } => Some(body),
438        _ => None,
439    }
440}
441
442// ---------------------------------------------------------------------------
443// The formatter
444// ---------------------------------------------------------------------------
445
446struct Formatter<'a> {
447    source: &'a str,
448    comments: Vec<Comment>,
449    /// The first comment that has not been emitted yet.
450    next: usize,
451    /// How far into the source everything emitted so far reaches, used to
452    /// find the blank lines and comments that come next.
453    pos: usize,
454    out: Out,
455}
456
457impl<'a> Formatter<'a> {
458    fn new(source: &'a str) -> Self {
459        Formatter {
460            source,
461            comments: scan_comments(source),
462            next: 0,
463            pos: 0,
464            out: Out::default(),
465        }
466    }
467
468    fn finish(self) -> String {
469        self.out.finish()
470    }
471
472    // -- source helpers ----------------------------------------------------
473
474    fn text(&self, span: Span) -> Option<&'a str> {
475        self.source.get(span.start as usize..span.end as usize)
476    }
477
478    /// The source spelling of a code-point literal, so that `'a'` survives
479    /// formatting rather than being rewritten to `97`.
480    ///
481    /// A code-point literal is an `Int` and nothing past the lexer knows it
482    /// was written any other way, so without this the first `cove fmt` would
483    /// quietly delete the whole point of the form.
484    ///
485    /// It is a predicate of its own rather than a widening of `number_text`
486    /// because that one refuses a span holding whitespace, and `' '` is a
487    /// code-point literal whose entire content is a space.
488    fn code_point_text(&self, span: Span) -> Option<&'a str> {
489        let text = self.text(span)?;
490        let mut characters = text.chars();
491        (characters.next() == Some('\'') && characters.next_back() == Some('\'')).then_some(text)
492    }
493
494    /// The source spelling of a numeric literal, so that `0xff`, `1_000`, and
495    /// `60s` survive formatting.
496    fn number_text(&self, span: Span) -> Option<&'a str> {
497        let text = self.text(span)?;
498        let first = text.chars().next()?;
499        if !first.is_ascii_digit() || text.chars().any(char::is_whitespace) {
500            return None;
501        }
502        Some(text)
503    }
504
505    /// The source spelling of a string literal, escapes and interpolations
506    /// included. A string is reproduced rather than rebuilt because its
507    /// interpolations cannot be broken across lines.
508    fn string_text(&self, span: Span) -> Option<&'a str> {
509        let text = self.text(span)?;
510        if text.len() >= 2 && text.starts_with('"') && text.ends_with('"') {
511            Some(text)
512        } else {
513            None
514        }
515    }
516
517    /// Whether a blank line separates `from` from `to` in the source.
518    fn blank_line_between(&self, from: usize, to: usize) -> bool {
519        if from >= to {
520            return false;
521        }
522        let Some(text) = self.source.get(from..to) else {
523            return false;
524        };
525        let lines: Vec<&str> = text.split('\n').collect();
526        lines.len() >= 3
527            && lines[1..lines.len() - 1]
528                .iter()
529                .any(|l| l.trim().is_empty())
530    }
531
532    /// Whether an unemitted comment falls between `from` and `to`.
533    fn comment_between(&self, from: u32, to: u32) -> bool {
534        self.comments[self.next..]
535            .iter()
536            .any(|c| c.start >= from as usize && c.start < to as usize)
537    }
538
539    /// Whether an unemitted comment falls inside `span`.
540    ///
541    /// Such a comment can only be kept where the author wrote it if the
542    /// construct is laid out across lines, so this forces the construct to
543    /// break.
544    fn holds_comment(&self, span: Span) -> bool {
545        self.comment_between(span.start, span.end)
546    }
547
548    fn advance(&mut self, to: u32) {
549        self.pos = self.pos.max(to as usize);
550    }
551
552    // -- comment placement -------------------------------------------------
553
554    /// Emits every comment that precedes `start`, plus the blank lines around
555    /// them, at `indent`.
556    ///
557    /// `allow_blank` is false at the start of a file or a block, where a
558    /// leading blank line is never kept.
559    fn lead(&mut self, start: usize, indent: usize, allow_blank: bool) {
560        let mut allow = allow_blank;
561        while self.next < self.comments.len() && self.comments[self.next].start < start {
562            let comment = self.comments[self.next].clone();
563            if allow && self.blank_line_between(self.pos, comment.start) {
564                self.out.pending_blank = true;
565            }
566            allow = true;
567            self.out.start_line(indent);
568            self.write_comment(&comment, indent);
569            self.pos = self.pos.max(comment.end);
570            self.next += 1;
571        }
572        if allow && self.blank_line_between(self.pos, start) {
573            self.out.pending_blank = true;
574        }
575    }
576
577    /// Emits the comments that precede a closing `}` at `start`, without the
578    /// blank line that would otherwise separate them from it.
579    fn lead_close(&mut self, start: usize, indent: usize) {
580        self.lead(start, indent, true);
581        self.out.pending_blank = false;
582    }
583
584    /// Attaches the comment that follows `end` on the same source line to the
585    /// line just written.
586    fn trail(&mut self, end: u32) {
587        let end = end as usize;
588        while self.next < self.comments.len() {
589            let comment = &self.comments[self.next];
590            if comment.own_line || comment.start < end || comment.text.contains('\n') {
591                return;
592            }
593            match self.source.get(end..comment.start) {
594                Some(between) if !between.contains('\n') => {}
595                _ => return,
596            }
597            let text = comment.text.clone();
598            let comment_end = comment.end;
599            self.out.set_comment(&text);
600            self.pos = self.pos.max(comment_end);
601            self.next += 1;
602        }
603    }
604
605    /// Writes a comment, re-indenting the continuation lines of a multi-line
606    /// block comment by the same amount as its first line moved.
607    fn write_comment(&mut self, comment: &Comment, indent: usize) {
608        let mut lines = comment.text.split('\n');
609        if let Some(first) = lines.next() {
610            self.out.write(first);
611        }
612        let prefix = " ".repeat(comment.column);
613        for line in lines {
614            self.out.start_line(indent);
615            let rest = line
616                .strip_prefix(prefix.as_str())
617                .unwrap_or(line.trim_start());
618            self.out.write(rest);
619        }
620    }
621
622    // -- top level ---------------------------------------------------------
623
624    fn source_unit(&mut self, unit: &SourceUnit) {
625        let first_item = unit
626            .items
627            .first()
628            .map(|item| item.span.start as usize)
629            .unwrap_or(usize::MAX);
630
631        for use_decl in &unit.uses {
632            let limit = (use_decl.span.start as usize).min(first_item);
633            self.lead(limit, 0, false);
634            self.out.start_line(0);
635            self.use_decl(use_decl);
636            self.advance(use_decl.span.end);
637            self.trail(use_decl.span.end);
638        }
639
640        for (i, item) in unit.items.iter().enumerate() {
641            if i > 0 || !unit.uses.is_empty() {
642                self.out.pending_blank = true;
643            }
644            self.lead(item.span.start as usize, 0, true);
645            self.item(item, 0);
646            self.advance(item.span.end);
647            self.trail(item.span.end);
648        }
649
650        self.lead(self.source.len(), 0, true);
651    }
652
653    fn use_decl(&mut self, use_decl: &Use) {
654        self.out.write("use ");
655        let path: Vec<&str> = use_decl.path.iter().map(|s| s.node.as_str()).collect();
656        self.out.write(&path.join("."));
657    }
658
659    fn item(&mut self, item: &Item, indent: usize) {
660        if let Some(doc) = &item.doc {
661            self.doc_comment(doc, indent);
662        }
663        self.out.start_line(indent);
664        if item.exported {
665            self.out.write("export ");
666        }
667        if item.is_opaque {
668            self.out.write("opaque ");
669        }
670        if item.is_test {
671            self.out.write("test ");
672        }
673        match &item.kind {
674            ItemKind::Fn(decl) => self.fn_decl(decl, indent),
675            ItemKind::Struct(decl) => self.struct_decl(decl, indent),
676            ItemKind::Enum(decl) => self.enum_decl(decl, indent),
677            ItemKind::Trait(decl) => self.trait_decl(decl, indent),
678            ItemKind::Impl(block) => self.impl_block(block, indent),
679            ItemKind::TypeAlias(alias) => self.type_alias(alias, indent),
680        }
681    }
682
683    /// Writes a `///` comment directly above its declaration, with no blank
684    /// line in between.
685    fn doc_comment(&mut self, doc: &str, indent: usize) {
686        for line in doc.split('\n') {
687            self.out.start_line(indent);
688            if line.is_empty() {
689                self.out.write("///");
690            } else {
691                self.out.write("/// ");
692                self.out.write(line);
693            }
694        }
695        self.out.pending_blank = false;
696    }
697
698    /// `<T, U: Display + Ordered>`, or nothing.
699    fn generics(&mut self, generics: &[GenericParam]) {
700        if generics.is_empty() {
701            return;
702        }
703        let names: Vec<String> = generics.iter().map(GenericParam::to_string).collect();
704        self.out.write("<");
705        self.out.write(&names.join(", "));
706        self.out.write(">");
707    }
708
709    // -- declarations ------------------------------------------------------
710
711    fn fn_decl(&mut self, decl: &FnDecl, indent: usize) {
712        if decl.is_async {
713            self.out.write("async ");
714        }
715        self.out.write("fn ");
716        self.out.write(&decl.name.node);
717        self.generics(&decl.generics);
718        self.param_list(
719            decl.receiver,
720            &decl.params,
721            indent,
722            decl.return_type.as_ref(),
723        );
724        if let Some(return_type) = &decl.return_type {
725            self.out.write(" -> ");
726            self.type_ref(return_type, indent);
727        }
728        self.out.write(" ");
729        self.block(&decl.body, indent);
730    }
731
732    /// Writes `(...)`, breaking one parameter per line when the whole
733    /// signature does not fit.
734    fn param_list(
735        &mut self,
736        receiver: Option<Receiver>,
737        params: &[Param],
738        indent: usize,
739        return_type: Option<&Type>,
740    ) {
741        let mut entries: Vec<String> = Vec::new();
742        if let Some(receiver) = receiver {
743            entries.push(if receiver.is_var {
744                "var self".into()
745            } else {
746                "self".into()
747            });
748        }
749        entries.extend(params.iter().map(|p| self.param_flat(p)));
750
751        let flat = format!("({})", entries.join(", "));
752        let tail = match return_type {
753            Some(ty) => width(&ty.to_string()) + 4,
754            None => 0,
755        };
756        // Two more columns for the ` {` that opens the body.
757        if self.out.col() + width(&flat) + tail + 2 <= MAX_WIDTH {
758            self.out.write(&flat);
759            return;
760        }
761        self.out.write("(");
762        for entry in &entries {
763            self.out.start_line(indent + INDENT);
764            self.out.write(entry);
765            self.out.write(",");
766        }
767        self.out.start_line(indent);
768        self.out.write(")");
769    }
770
771    /// `[var ]name[: Type][...][ = default]`, the form a parameter is
772    /// declared in.
773    fn param_flat(&self, param: &Param) -> String {
774        let mut out = String::new();
775        if param.is_var {
776            out.push_str("var ");
777        }
778        if !param.name.node.is_empty() {
779            out.push_str(&param.name.node);
780            if param.ty.is_some() {
781                out.push_str(": ");
782            }
783        }
784        if let Some(ty) = &param.ty {
785            out.push_str(&ty.to_string());
786        }
787        if param.variadic {
788            out.push_str("...");
789        }
790        if let Some(default) = &param.default {
791            out.push_str(" = ");
792            out.push_str(&self.flat(default, prec::RETURN));
793        }
794        out
795    }
796
797    fn struct_decl(&mut self, decl: &StructDecl, indent: usize) {
798        self.out.write("struct ");
799        self.out.write(&decl.name.node);
800        self.generics(&decl.generics);
801        self.out.write(" ");
802        if decl.fields.is_empty() && !self.holds_comment(decl.span) {
803            self.out.write("{ }");
804            return;
805        }
806        self.out.write("{");
807        let inner = indent + INDENT;
808        for (i, field) in decl.fields.iter().enumerate() {
809            self.lead(field.span.start as usize, inner, i > 0);
810            self.field(field, inner);
811            self.advance(field.span.end);
812            self.trail(field.span.end);
813        }
814        self.lead_close(close_brace(decl.span.end), inner);
815        self.out.start_line(indent);
816        self.out.write("}");
817    }
818
819    fn field(&mut self, field: &Field, indent: usize) {
820        if let Some(doc) = &field.doc {
821            self.doc_comment(doc, indent);
822        }
823        self.out.start_line(indent);
824        self.out.write(&field.name.node);
825        self.out.write(": ");
826        self.type_ref(&field.ty, indent);
827    }
828
829    fn enum_decl(&mut self, decl: &EnumDecl, indent: usize) {
830        self.out.write("enum ");
831        self.out.write(&decl.name.node);
832        self.generics(&decl.generics);
833        self.out.write(" ");
834        if decl.cases.is_empty() && !self.holds_comment(decl.span) {
835            self.out.write("{ }");
836            return;
837        }
838        self.out.write("{");
839        let inner = indent + INDENT;
840        for (i, case) in decl.cases.iter().enumerate() {
841            self.lead(case.span.start as usize, inner, i > 0);
842            self.enum_case(case, inner);
843            self.advance(case.span.end);
844            self.trail(case.span.end);
845        }
846        self.lead_close(close_brace(decl.span.end), inner);
847        self.out.start_line(indent);
848        self.out.write("}");
849    }
850
851    fn enum_case(&mut self, case: &EnumCase, indent: usize) {
852        if let Some(doc) = &case.doc {
853            self.doc_comment(doc, indent);
854        }
855        self.out.start_line(indent);
856        self.out.write(&case.name.node);
857        if !case.payload.is_empty() {
858            let types: Vec<String> = case.payload.iter().map(Type::to_string).collect();
859            self.out.write("(");
860            self.out.write(&types.join(", "));
861            self.out.write(")");
862        }
863    }
864
865    /// `trait Name { ... }`, one method per line.
866    fn trait_decl(&mut self, decl: &TraitDecl, indent: usize) {
867        self.out.write("trait ");
868        self.out.write(&decl.name.node);
869        self.out.write(" ");
870        if decl.methods.is_empty() && !self.holds_comment(decl.span) {
871            self.out.write("{ }");
872            return;
873        }
874        self.out.write("{");
875        let inner = indent + INDENT;
876        for (i, method) in decl.methods.iter().enumerate() {
877            if i > 0 {
878                self.out.pending_blank = true;
879            }
880            self.lead(method.span.start as usize, inner, i > 0);
881            self.trait_method(method, inner);
882            self.advance(method.span.end);
883            self.trail(method.span.end);
884        }
885        self.lead_close(close_brace(decl.span.end), inner);
886        self.out.start_line(indent);
887        self.out.write("}");
888    }
889
890    /// One trait method: a signature, plus a default body when it has one.
891    fn trait_method(&mut self, method: &TraitMethod, indent: usize) {
892        if let Some(doc) = &method.doc {
893            self.doc_comment(doc, indent);
894        }
895        self.out.start_line(indent);
896        if method.is_async {
897            self.out.write("async ");
898        }
899        self.out.write("fn ");
900        self.out.write(&method.name.node);
901        self.param_list(
902            method.receiver,
903            &method.params,
904            indent,
905            method.return_type.as_ref(),
906        );
907        if let Some(return_type) = &method.return_type {
908            self.out.write(" -> ");
909            self.type_ref(return_type, indent);
910        }
911        if let Some(default) = &method.default {
912            self.out.write(" ");
913            self.block(default, indent);
914        }
915    }
916
917    fn impl_block(&mut self, block: &ImplBlock, indent: usize) {
918        self.out.write("impl ");
919        if let Some(trait_name) = &block.trait_name {
920            self.out.write(&trait_name.node);
921            self.out.write(" for ");
922        }
923        self.out.write(&block.type_name.node);
924        self.generics(&block.generics);
925        self.out.write(" ");
926        if block.items.is_empty() && !self.holds_comment(block.span) {
927            self.out.write("{ }");
928            return;
929        }
930        self.out.write("{");
931        let inner = indent + INDENT;
932        for (i, item) in block.items.iter().enumerate() {
933            if i > 0 {
934                self.out.pending_blank = true;
935            }
936            self.lead(item.span.start as usize, inner, i > 0);
937            self.item(item, inner);
938            self.advance(item.span.end);
939            self.trail(item.span.end);
940        }
941        self.lead_close(close_brace(block.span.end), inner);
942        self.out.start_line(indent);
943        self.out.write("}");
944    }
945
946    fn type_alias(&mut self, alias: &TypeAlias, indent: usize) {
947        self.out.write("type ");
948        self.out.write(&alias.name.node);
949        self.generics(&alias.generics);
950        self.out.write(" = ");
951        self.type_ref(&alias.ty, indent);
952    }
953
954    // -- types -------------------------------------------------------------
955
956    /// Writes a type, breaking a function type's parameters one per line when
957    /// the whole type does not fit.
958    fn type_ref(&mut self, ty: &Type, indent: usize) {
959        let flat = ty.to_string();
960        if self.out.col() + width(&flat) <= MAX_WIDTH {
961            self.out.write(&flat);
962            return;
963        }
964        let TypeKind::Fn {
965            is_async,
966            params,
967            return_type,
968        } = &ty.kind
969        else {
970            self.out.write(&flat);
971            return;
972        };
973        if *is_async {
974            self.out.write("async ");
975        }
976        self.out.write("fn(");
977        for param in params {
978            self.out.start_line(indent + INDENT);
979            self.out.write(&param.to_string());
980            self.out.write(",");
981        }
982        self.out.start_line(indent);
983        self.out.write(")");
984        if let Some(return_type) = return_type {
985            self.out.write(" -> ");
986            self.type_ref(return_type, indent);
987        }
988    }
989
990    // -- blocks and statements ---------------------------------------------
991
992    /// Writes `{ ... }` starting at the current column.
993    fn block(&mut self, block: &Block, indent: usize) {
994        if block_is_empty(block) && !self.holds_comment(block.span) {
995            self.out.write("{ }");
996            self.advance(block.span.end);
997            return;
998        }
999        self.out.write("{");
1000        self.advance(block.span.start + 1);
1001        let inner = indent + INDENT;
1002        let mut first = true;
1003        for stmt in &block.statements {
1004            self.lead(stmt.span.start as usize, inner, !first);
1005            self.stmt(stmt, inner);
1006            self.advance(stmt.span.end);
1007            self.trail(stmt.span.end);
1008            first = false;
1009        }
1010        if let Some(tail) = &block.tail {
1011            self.lead(tail.span.start as usize, inner, !first);
1012            self.out.start_line(inner);
1013            self.expr(tail, prec::RETURN, inner);
1014            self.advance(tail.span.end);
1015            self.trail(tail.span.end);
1016        }
1017        self.lead_close(close_brace(block.span.end), inner);
1018        self.out.start_line(indent);
1019        self.out.write("}");
1020        self.advance(block.span.end);
1021    }
1022
1023    fn stmt(&mut self, stmt: &Stmt, indent: usize) {
1024        match &stmt.kind {
1025            StmtKind::Item(item) => self.item(item, indent),
1026            StmtKind::Let {
1027                is_var,
1028                name,
1029                ty,
1030                value,
1031            } => {
1032                self.out.start_line(indent);
1033                self.out.write(if *is_var { "var " } else { "let " });
1034                self.out.write(&name.node);
1035                if let Some(ty) = ty {
1036                    self.out.write(": ");
1037                    self.type_ref(ty, indent);
1038                }
1039                self.out.write(" = ");
1040                self.expr(value, prec::RETURN, indent);
1041            }
1042            StmtKind::Expr(value) => {
1043                self.out.start_line(indent);
1044                self.expr(value, prec::RETURN, indent);
1045            }
1046        }
1047    }
1048}
1049
1050// ---------------------------------------------------------------------------
1051// Postfix chains
1052// ---------------------------------------------------------------------------
1053
1054/// One postfix operator applied to the base of a chain.
1055enum Post<'e> {
1056    Field(&'e str),
1057    Call {
1058        generics: &'e [Type],
1059        args: &'e [Arg],
1060        trailing: Option<&'e Expr>,
1061    },
1062    Try,
1063}
1064
1065/// Splits `a.b(x).c(y)?` into its base and the postfix operators applied to
1066/// it, so that a chain too long for one line can break before its dots.
1067fn flatten_postfix(expr: &Expr) -> (&Expr, Vec<Post<'_>>) {
1068    match &expr.kind {
1069        ExprKind::Field { base, name } => {
1070            let (base, mut ops) = flatten_postfix(base);
1071            ops.push(Post::Field(name.node.as_str()));
1072            (base, ops)
1073        }
1074        ExprKind::Call {
1075            callee,
1076            generics,
1077            args,
1078            trailing,
1079        } => {
1080            let (base, mut ops) = flatten_postfix(callee);
1081            ops.push(Post::Call {
1082                generics,
1083                args,
1084                trailing: trailing.as_deref(),
1085            });
1086            (base, ops)
1087        }
1088        ExprKind::Try(inner) => {
1089            let (base, mut ops) = flatten_postfix(inner);
1090            ops.push(Post::Try);
1091            (base, ops)
1092        }
1093        _ => (expr, Vec::new()),
1094    }
1095}
1096
1097/// The indices of the dots a method chain may break before: every field
1098/// access that follows a call.
1099fn chain_break_points(ops: &[Post<'_>]) -> Vec<usize> {
1100    let mut points = Vec::new();
1101    let mut seen_call = false;
1102    for (i, op) in ops.iter().enumerate() {
1103        match op {
1104            Post::Call { .. } => seen_call = true,
1105            Post::Field(_) if seen_call => points.push(i),
1106            _ => {}
1107        }
1108    }
1109    points
1110}
1111
1112/// Whether hugging the last argument of a call is worth doing.
1113///
1114/// A closure always is: the call site reads as one call with a body. Anything
1115/// else only is when it is the whole argument list, so that a call such as
1116/// `push(Route(...))` expands its initializer in place while a call with
1117/// several arguments still breaks one argument per line.
1118fn hug_applies(closure: bool, last: &Arg, earlier: &[Arg]) -> bool {
1119    closure || (earlier.is_empty() && last.label.is_none() && !last.is_var && !last.spread)
1120}
1121
1122fn call_count(ops: &[Post<'_>]) -> usize {
1123    ops.iter()
1124        .filter(|op| matches!(op, Post::Call { .. }))
1125        .count()
1126}
1127
1128// ---------------------------------------------------------------------------
1129// Expressions
1130// ---------------------------------------------------------------------------
1131
1132impl Formatter<'_> {
1133    /// Whether `expr` must be laid out across several lines.
1134    ///
1135    /// This is a layout policy, not a limit: a declaration body, a control
1136    /// flow body, and a lambda body always read better broken, and an
1137    /// expression holding a comment can only keep it where it was written if
1138    /// it breaks.
1139    fn breaks(&self, expr: &Expr) -> bool {
1140        if self.holds_comment(expr.span) {
1141            return true;
1142        }
1143        match &expr.kind {
1144            ExprKind::Int(_)
1145            | ExprKind::Float(_)
1146            | ExprKind::Bool(_)
1147            | ExprKind::Duration(_)
1148            | ExprKind::Str(_)
1149            | ExprKind::Unit
1150            | ExprKind::Ident(_) => false,
1151            ExprKind::ArrayLit(elements) => elements.iter().any(|e| self.breaks(e)),
1152            ExprKind::Field { base, .. } => self.breaks(base),
1153            ExprKind::Call {
1154                callee,
1155                args,
1156                trailing,
1157                ..
1158            } => {
1159                self.breaks(callee)
1160                    || args.iter().any(|arg| self.breaks(&arg.value))
1161                    || trailing.as_deref().is_some_and(|t| self.trailing_breaks(t))
1162            }
1163            ExprKind::Unary { operand, .. } => self.breaks(operand),
1164            ExprKind::Binary { lhs, rhs, .. } => self.breaks(lhs) || self.breaks(rhs),
1165            ExprKind::Assign { target, value, .. } => self.breaks(target) || self.breaks(value),
1166            ExprKind::Try(inner) | ExprKind::Await(inner) => self.breaks(inner),
1167            ExprKind::Block(block) => !block_is_empty(block),
1168            ExprKind::If { .. }
1169            | ExprKind::Match { .. }
1170            | ExprKind::For { .. }
1171            | ExprKind::While { .. }
1172            | ExprKind::Scope { .. } => true,
1173            ExprKind::Return(value) | ExprKind::Break(value) => {
1174                value.as_deref().is_some_and(|v| self.breaks(v))
1175            }
1176            ExprKind::Continue => false,
1177            ExprKind::Lambda { body, .. } => !block_is_empty(body),
1178            ExprKind::Range { start, end, .. } => self.breaks(start) || self.breaks(end),
1179        }
1180    }
1181
1182    /// A trailing closure stays on one line when its body is a single
1183    /// expression that fits, so `tasks.spawn { fetch() }` reads as the one
1184    /// call it is.
1185    fn trailing_breaks(&self, closure: &Expr) -> bool {
1186        let Some(body) = trailing_body(closure) else {
1187            return self.breaks(closure);
1188        };
1189        if self.holds_comment(body.span) || !body.statements.is_empty() {
1190            return true;
1191        }
1192        body.tail.as_deref().is_some_and(|tail| self.breaks(tail))
1193    }
1194
1195    /// Writes `expr`, parenthesised when its precedence is below `min`, and
1196    /// broken across lines when it must be or does not fit.
1197    fn expr(&mut self, expr: &Expr, min: u8, indent: usize) {
1198        if expr_prec(expr) < min {
1199            self.out.write("(");
1200            self.expr(expr, prec::RETURN, indent);
1201            self.out.write(")");
1202            return;
1203        }
1204        if !self.breaks(expr) {
1205            let flat = self.flat_inner(expr);
1206            if self.out.col() + width(&flat) <= MAX_WIDTH {
1207                self.out.write(&flat);
1208                return;
1209            }
1210        }
1211        self.expr_broken(expr, indent);
1212    }
1213
1214    /// Writes a header expression — the condition of `if` or `while`, the
1215    /// iterable of `for`, the scrutinee of `match` — parenthesising it when
1216    /// it would otherwise end in a `{` the parser would read as the body.
1217    fn header(&mut self, expr: &Expr, indent: usize) {
1218        if ends_with_brace(expr) {
1219            self.out.write("(");
1220            self.expr(expr, prec::RETURN, indent);
1221            self.out.write(")");
1222        } else {
1223            self.expr(expr, prec::RETURN, indent);
1224        }
1225    }
1226
1227    fn expr_broken(&mut self, expr: &Expr, indent: usize) {
1228        match &expr.kind {
1229            ExprKind::Call { .. } => self.call(expr, indent),
1230            ExprKind::ArrayLit(elements) => self.array_literal(elements, expr.span, indent),
1231            ExprKind::Field { base, name } => {
1232                self.expr(base, prec::POSTFIX, indent);
1233                self.out.write(".");
1234                self.out.write(&name.node);
1235            }
1236            ExprKind::Unary { op, operand } => {
1237                self.out.write(unary_symbol(*op));
1238                self.expr(operand, prec::UNARY, indent);
1239            }
1240            ExprKind::Binary { op, lhs, rhs } => self.binary(*op, lhs, rhs, indent),
1241            ExprKind::Assign { op, target, value } => {
1242                self.expr(target, prec::POSTFIX, indent);
1243                match op {
1244                    Some(op) => {
1245                        self.out.write(" ");
1246                        self.out.write(binary_symbol(*op));
1247                        self.out.write("= ");
1248                    }
1249                    None => self.out.write(" = "),
1250                }
1251                self.expr(value, prec::ASSIGN, indent);
1252            }
1253            // `await x?` is how the parser builds `Try(Await(x))`, so it is
1254            // also how the formatter writes it back.
1255            ExprKind::Try(inner) => match &inner.kind {
1256                ExprKind::Await(awaited) => {
1257                    self.out.write("await ");
1258                    self.expr(awaited, prec::POSTFIX, indent);
1259                    self.out.write("?");
1260                }
1261                _ => {
1262                    self.expr(inner, prec::POSTFIX, indent);
1263                    self.out.write("?");
1264                }
1265            },
1266            ExprKind::Await(inner) => {
1267                self.out.write("await ");
1268                self.expr(inner, prec::POSTFIX, indent);
1269            }
1270            ExprKind::Block(block) => self.block(block, indent),
1271            ExprKind::If { .. } => self.if_expr(expr, indent),
1272            ExprKind::Match { scrutinee, arms } => {
1273                self.match_expr(scrutinee, arms, expr.span, indent)
1274            }
1275            ExprKind::For {
1276                binding,
1277                iterable,
1278                body,
1279            } => {
1280                self.out.write("for ");
1281                self.out.write(&binding.node);
1282                self.out.write(" in ");
1283                self.header(iterable, indent);
1284                self.out.write(" ");
1285                self.block(body, indent);
1286            }
1287            ExprKind::While { condition, body } => {
1288                self.out.write("while ");
1289                self.header(condition, indent);
1290                self.out.write(" ");
1291                self.block(body, indent);
1292            }
1293            ExprKind::Scope { name, body } => {
1294                self.out.write("scope ");
1295                self.out.write(&name.node);
1296                self.out.write(" ");
1297                self.block(body, indent);
1298            }
1299            ExprKind::Return(value) => {
1300                self.out.write("return");
1301                if let Some(value) = value {
1302                    self.out.write(" ");
1303                    self.expr(value, prec::RETURN, indent);
1304                }
1305            }
1306            ExprKind::Break(value) => {
1307                self.out.write("break");
1308                if let Some(value) = value {
1309                    self.out.write(" ");
1310                    self.expr(value, prec::RETURN, indent);
1311                }
1312            }
1313            ExprKind::Continue => self.out.write("continue"),
1314            ExprKind::Lambda {
1315                is_async,
1316                params,
1317                body,
1318            } => self.lambda(*is_async, params, body, indent),
1319            ExprKind::Range {
1320                start,
1321                end,
1322                inclusive_end,
1323            } => {
1324                self.expr(start, prec::ADDITIVE, indent);
1325                self.out.write(if *inclusive_end { ".." } else { "..<" });
1326                self.expr(end, prec::ADDITIVE, indent);
1327            }
1328            _ => {
1329                let flat = self.flat_inner(expr);
1330                self.out.write(&flat);
1331            }
1332        }
1333    }
1334
1335    /// Writes a binary expression, breaking *after* the operator when the
1336    /// right-hand side does not fit: an operator that ends a line continues
1337    /// the expression onto the next one, while an operator that starts a line
1338    /// is an error.
1339    fn binary(&mut self, op: BinaryOp, lhs: &Expr, rhs: &Expr, indent: usize) {
1340        let level = binary_prec(op);
1341        self.expr(lhs, level, indent);
1342        self.out.write(" ");
1343        self.out.write(binary_symbol(op));
1344        if self.breaks(rhs) {
1345            self.out.write(" ");
1346            self.expr(rhs, level + 1, indent);
1347            return;
1348        }
1349        let flat = self.flat(rhs, level + 1);
1350        if self.out.col() + 1 + width(&flat) <= MAX_WIDTH {
1351            self.out.write(" ");
1352            self.out.write(&flat);
1353        } else {
1354            self.out.start_line(indent + INDENT);
1355            self.expr(rhs, level + 1, indent + INDENT);
1356        }
1357    }
1358
1359    fn if_expr(&mut self, expr: &Expr, indent: usize) {
1360        let ExprKind::If {
1361            condition,
1362            then_branch,
1363            else_branch,
1364        } = &expr.kind
1365        else {
1366            return;
1367        };
1368        self.out.write("if ");
1369        self.header(condition, indent);
1370        self.out.write(" ");
1371        self.block(then_branch, indent);
1372        let Some(else_branch) = else_branch else {
1373            return;
1374        };
1375        self.out.write(" else ");
1376        match &else_branch.kind {
1377            ExprKind::Block(block) => self.block(block, indent),
1378            ExprKind::If { .. } => self.if_expr(else_branch, indent),
1379            _ => self.expr(else_branch, prec::RETURN, indent),
1380        }
1381    }
1382
1383    fn match_expr(&mut self, scrutinee: &Expr, arms: &[MatchArm], span: Span, indent: usize) {
1384        self.out.write("match ");
1385        self.header(scrutinee, indent);
1386        self.out.write(" ");
1387        if arms.is_empty() && !self.holds_comment(span) {
1388            self.out.write("{ }");
1389            self.advance(span.end);
1390            return;
1391        }
1392        self.out.write("{");
1393        let inner = indent + INDENT;
1394        for (i, arm) in arms.iter().enumerate() {
1395            self.lead(arm.span.start as usize, inner, i > 0);
1396            self.out.start_line(inner);
1397            self.out.write(&self.pattern_flat(&arm.pattern));
1398            self.out.write(" => ");
1399            self.expr(&arm.body, prec::RETURN, inner);
1400            self.advance(arm.span.end);
1401            self.trail(arm.span.end);
1402        }
1403        self.lead_close(close_brace(span.end), inner);
1404        self.out.start_line(indent);
1405        self.out.write("}");
1406        self.advance(span.end);
1407    }
1408
1409    /// Writes `fn(x) { ... }`. A lambda always shows its parameter list, even
1410    /// when it is empty, so that `fn() { ... }` is never mistaken for the
1411    /// braces of a trailing closure.
1412    fn lambda(&mut self, is_async: bool, params: &[Param], body: &Block, indent: usize) {
1413        if is_async {
1414            self.out.write("async ");
1415        }
1416        self.out.write("fn");
1417        self.param_list(None, params, indent, None);
1418        self.out.write(" ");
1419        self.block(body, indent);
1420    }
1421
1422    /// The first line a lambda would occupy, used to decide whether a call
1423    /// can keep its arguments on one line and let the closure expand below.
1424    fn lambda_header(&self, is_async: bool, params: &[Param]) -> String {
1425        let mut head = String::new();
1426        if is_async {
1427            head.push_str("async ");
1428        }
1429        head.push_str("fn(");
1430        let entries: Vec<String> = params.iter().map(|p| self.param_flat(p)).collect();
1431        head.push_str(&entries.join(", "));
1432        head.push_str(") {");
1433        head
1434    }
1435
1436    fn call(&mut self, expr: &Expr, indent: usize) {
1437        let ExprKind::Call {
1438            callee,
1439            generics,
1440            args,
1441            trailing,
1442        } = &expr.kind
1443        else {
1444            return;
1445        };
1446
1447        // A chain of calls that is merely too long breaks before its dots.
1448        if !self.breaks(expr) {
1449            let (base, ops) = flatten_postfix(expr);
1450            let points = chain_break_points(&ops);
1451            if call_count(&ops) >= 2 && !points.is_empty() {
1452                self.chain(base, &ops, &points, expr.span, indent);
1453                return;
1454            }
1455        }
1456
1457        self.expr(callee, prec::POSTFIX, indent);
1458        self.generic_args(generics);
1459        self.call_tail(args, trailing.as_deref(), generics, expr.span, indent);
1460    }
1461
1462    /// Writes the argument list, and the trailing closure when there is one.
1463    fn call_tail(
1464        &mut self,
1465        args: &[Arg],
1466        trailing: Option<&Expr>,
1467        generics: &[Type],
1468        span: Span,
1469        indent: usize,
1470    ) {
1471        // `tasks.spawn { ... }` writes no parentheses at all; a generic call
1472        // always does, because the parser only reads `<T>` as a type list
1473        // when a `(` follows it.
1474        let parens = !(args.is_empty() && trailing.is_some() && generics.is_empty());
1475        if parens {
1476            self.arg_list(args, trailing.is_some(), span, indent);
1477        }
1478        if let Some(trailing) = trailing {
1479            self.out.write(" ");
1480            match trailing_body(trailing) {
1481                Some(body) => self.block(body, indent),
1482                None => self.expr(trailing, prec::RETURN, indent),
1483            }
1484        }
1485    }
1486
1487    fn arg_list(&mut self, args: &[Arg], has_trailing: bool, span: Span, indent: usize) {
1488        // A comment written between the arguments only stays where it was if
1489        // the list breaks, so it counts as a reason to break.
1490        let region_end = if has_trailing {
1491            args.last().map(|arg| arg.span.end)
1492        } else {
1493            Some(span.end)
1494        };
1495        let commented = region_end.is_some_and(|end| self.comment_between(span.start, end));
1496
1497        let flat = format!("({})", self.args_flat(args));
1498        // Two more columns for the ` {` of a trailing closure.
1499        let reserved = if has_trailing { 2 } else { 0 };
1500        if !commented
1501            && !args.iter().any(|arg| self.breaks(&arg.value))
1502            && self.out.col() + width(&flat) + reserved <= MAX_WIDTH
1503        {
1504            self.out.write(&flat);
1505            return;
1506        }
1507
1508        // Breaking a lone argument that cannot itself break, and would not
1509        // fit on a line of its own either, only makes the call longer.
1510        if let [only] = args {
1511            let value = self.flat(&only.value, prec::RETURN);
1512            if !commented
1513                && !self.breaks(&only.value)
1514                && only.label.is_none()
1515                && !only.is_var
1516                && !only.spread
1517                && indent + INDENT + width(&value) + 1 > MAX_WIDTH
1518            {
1519                self.out.write(&flat);
1520                return;
1521            }
1522        }
1523
1524        // A final closure, call, or array argument keeps the call's head on
1525        // one line and expands below it, which is how a callback and a
1526        // struct initializer read at the call site.
1527        if !has_trailing && self.hug_last(args, span, indent) {
1528            return;
1529        }
1530
1531        self.out.write("(");
1532        for (i, arg) in args.iter().enumerate() {
1533            self.lead(arg.span.start as usize, indent + INDENT, i > 0);
1534            self.out.start_line(indent + INDENT);
1535            self.arg_prefix(arg);
1536            self.expr(&arg.value, prec::RETURN, indent + INDENT);
1537            self.out.write(",");
1538            self.advance(arg.span.end);
1539            self.trail(arg.span.end);
1540        }
1541        if !has_trailing {
1542            self.lead_close(close_brace(span.end), indent + INDENT);
1543        }
1544        self.out.start_line(indent);
1545        self.out.write(")");
1546    }
1547
1548    /// The first line the last argument occupies when a call hugs it: the
1549    /// header of a closure, the `[` of an array, or the callee and `(` of a
1550    /// nested call, together with whether that head bottoms out in a closure.
1551    ///
1552    /// `None` when the argument has no such head and so cannot be hugged.
1553    fn hug_head(&self, expr: &Expr) -> Option<(String, bool)> {
1554        match &expr.kind {
1555            ExprKind::Lambda {
1556                is_async, params, ..
1557            } => Some((self.lambda_header(*is_async, params), true)),
1558            ExprKind::ArrayLit(elements) if !elements.is_empty() => Some(("[".to_string(), false)),
1559            ExprKind::Call {
1560                callee,
1561                generics,
1562                args,
1563                trailing,
1564            } if trailing.is_none() && !args.is_empty() => {
1565                let mut head = self.flat(callee, prec::POSTFIX);
1566                if !generics.is_empty() {
1567                    let names: Vec<String> = generics.iter().map(Type::to_string).collect();
1568                    head.push('<');
1569                    head.push_str(&names.join(", "));
1570                    head.push('>');
1571                }
1572                head.push('(');
1573                let mut closure = false;
1574                if let Some((last, earlier)) = args.split_last() {
1575                    if let Some((inner, inner_closure)) = self.hug_head(&last.value) {
1576                        if hug_applies(inner_closure, last, earlier)
1577                            && !earlier.iter().any(|arg| self.breaks(&arg.value))
1578                        {
1579                            for arg in earlier {
1580                                head.push_str(&self.arg_flat(arg));
1581                                head.push_str(", ");
1582                            }
1583                            head.push_str(&self.arg_prefix_text(last));
1584                            head.push_str(&inner);
1585                            closure = inner_closure;
1586                        }
1587                    }
1588                }
1589                Some((head, closure))
1590            }
1591            _ => None,
1592        }
1593    }
1594
1595    /// Writes `f(a, b, fn(x) { ... })` with the last argument expanded in
1596    /// place, or reports that the shape does not apply here.
1597    fn hug_last(&mut self, args: &[Arg], span: Span, indent: usize) -> bool {
1598        let Some((last, earlier)) = args.split_last() else {
1599            return false;
1600        };
1601        if earlier.iter().any(|arg| self.breaks(&arg.value))
1602            || self.comment_between(span.start, last.span.start)
1603        {
1604            return false;
1605        }
1606        let Some((inner_head, closure)) = self.hug_head(&last.value) else {
1607            return false;
1608        };
1609        if !hug_applies(closure, last, earlier) {
1610            return false;
1611        }
1612
1613        let mut prefix = String::from("(");
1614        for arg in earlier {
1615            prefix.push_str(&self.arg_flat(arg));
1616            prefix.push_str(", ");
1617        }
1618        prefix.push_str(&self.arg_prefix_text(last));
1619        let column = self.out.col() + width(&prefix);
1620        if column + width(&inner_head) > MAX_WIDTH {
1621            return false;
1622        }
1623        // Hugging only helps when the argument really does expand below the
1624        // head; one that still fits on this line would leave the call as long
1625        // as it already was.
1626        if !self.breaks(&last.value)
1627            && column + width(&self.flat(&last.value, prec::RETURN)) <= MAX_WIDTH
1628        {
1629            return false;
1630        }
1631
1632        self.out.write(&prefix);
1633        self.expr(&last.value, prec::RETURN, indent);
1634        self.advance(last.span.end);
1635        self.out.write(")");
1636        true
1637    }
1638
1639    fn array_literal(&mut self, elements: &[Expr], span: Span, indent: usize) {
1640        self.out.write("[");
1641        for element in elements {
1642            self.lead(element.span.start as usize, indent + INDENT, false);
1643            self.out.start_line(indent + INDENT);
1644            self.expr(element, prec::RETURN, indent + INDENT);
1645            self.out.write(",");
1646            self.advance(element.span.end);
1647            self.trail(element.span.end);
1648        }
1649        self.lead_close(close_brace(span.end), indent + INDENT);
1650        self.out.start_line(indent);
1651        self.out.write("]");
1652        self.advance(span.end);
1653    }
1654
1655    /// Writes a method chain broken before each dot that follows a call.
1656    fn chain(
1657        &mut self,
1658        base: &Expr,
1659        ops: &[Post<'_>],
1660        points: &[usize],
1661        span: Span,
1662        indent: usize,
1663    ) {
1664        self.expr(base, prec::POSTFIX, indent);
1665        let mut level = indent;
1666        for (i, op) in ops.iter().enumerate() {
1667            match op {
1668                Post::Field(name) => {
1669                    if points.contains(&i) {
1670                        level = indent + INDENT;
1671                        self.out.start_line(level);
1672                    }
1673                    self.out.write(".");
1674                    self.out.write(name);
1675                }
1676                Post::Call {
1677                    generics,
1678                    args,
1679                    trailing,
1680                } => {
1681                    self.generic_args(generics);
1682                    self.call_tail(args, *trailing, generics, span, level);
1683                }
1684                Post::Try => self.out.write("?"),
1685            }
1686        }
1687    }
1688
1689    fn generic_args(&mut self, generics: &[Type]) {
1690        if generics.is_empty() {
1691            return;
1692        }
1693        let names: Vec<String> = generics.iter().map(Type::to_string).collect();
1694        self.out.write("<");
1695        self.out.write(&names.join(", "));
1696        self.out.write(">");
1697    }
1698
1699    fn arg_prefix(&mut self, arg: &Arg) {
1700        let prefix = self.arg_prefix_text(arg);
1701        self.out.write(&prefix);
1702    }
1703
1704    fn arg_prefix_text(&self, arg: &Arg) -> String {
1705        let mut out = String::new();
1706        if let Some(label) = &arg.label {
1707            out.push_str(&label.node);
1708            out.push_str(": ");
1709        }
1710        if arg.is_var {
1711            out.push_str("var ");
1712        }
1713        if arg.spread {
1714            out.push_str("...");
1715        }
1716        out
1717    }
1718}
1719
1720// ---------------------------------------------------------------------------
1721// One-line rendering
1722// ---------------------------------------------------------------------------
1723
1724/// The largest duration unit that divides `ns` exactly.
1725///
1726/// Used only when the source spelling is unavailable, since a duration
1727/// literal is stored as a nanosecond count.
1728fn duration_text(ns: i64) -> String {
1729    if ns == 0 {
1730        return "0ns".to_string();
1731    }
1732    for (factor, unit) in [
1733        (3_600_000_000_000i64, "h"),
1734        (60_000_000_000, "m"),
1735        (1_000_000_000, "s"),
1736        (1_000_000, "ms"),
1737        (1_000, "us"),
1738        (1, "ns"),
1739    ] {
1740        if ns % factor == 0 {
1741            return format!("{}{unit}", ns / factor);
1742        }
1743    }
1744    format!("{ns}ns")
1745}
1746
1747impl Formatter<'_> {
1748    /// Renders `expr` on one line, parenthesised when its precedence is below
1749    /// `min`.
1750    fn flat(&self, expr: &Expr, min: u8) -> String {
1751        let inner = self.flat_inner(expr);
1752        if expr_prec(expr) < min {
1753            format!("({inner})")
1754        } else {
1755            inner
1756        }
1757    }
1758
1759    fn flat_header(&self, expr: &Expr) -> String {
1760        if ends_with_brace(expr) {
1761            format!("({})", self.flat_inner(expr))
1762        } else {
1763            self.flat_inner(expr)
1764        }
1765    }
1766
1767    fn flat_inner(&self, expr: &Expr) -> String {
1768        match &expr.kind {
1769            ExprKind::Int(value) => self
1770                .number_text(expr.span)
1771                .or_else(|| self.code_point_text(expr.span))
1772                .map(str::to_string)
1773                .unwrap_or_else(|| value.to_string()),
1774            ExprKind::Float(value) => self
1775                .number_text(expr.span)
1776                .map(str::to_string)
1777                .unwrap_or_else(|| format!("{value:?}")),
1778            ExprKind::Duration(ns) => self
1779                .number_text(expr.span)
1780                .map(str::to_string)
1781                .unwrap_or_else(|| duration_text(*ns)),
1782            ExprKind::Bool(value) => value.to_string(),
1783            ExprKind::Str(parts) => self
1784                .string_text(expr.span)
1785                .map(str::to_string)
1786                .unwrap_or_else(|| self.string_from_parts(parts)),
1787            ExprKind::Unit => "()".to_string(),
1788            ExprKind::Ident(name) => name.clone(),
1789            ExprKind::ArrayLit(elements) => {
1790                let items: Vec<String> = elements
1791                    .iter()
1792                    .map(|e| self.flat(e, prec::RETURN))
1793                    .collect();
1794                format!("[{}]", items.join(", "))
1795            }
1796            ExprKind::Field { base, name } => {
1797                format!("{}.{}", self.flat(base, prec::POSTFIX), name.node)
1798            }
1799            ExprKind::Call {
1800                callee,
1801                generics,
1802                args,
1803                trailing,
1804            } => {
1805                let mut out = self.flat(callee, prec::POSTFIX);
1806                if !generics.is_empty() {
1807                    let names: Vec<String> = generics.iter().map(Type::to_string).collect();
1808                    out.push('<');
1809                    out.push_str(&names.join(", "));
1810                    out.push('>');
1811                }
1812                if !(args.is_empty() && trailing.is_some() && generics.is_empty()) {
1813                    out.push('(');
1814                    out.push_str(&self.args_flat(args));
1815                    out.push(')');
1816                }
1817                if let Some(trailing) = trailing {
1818                    out.push(' ');
1819                    match trailing_body(trailing) {
1820                        Some(body) => out.push_str(&self.flat_block(body)),
1821                        None => out.push_str(&self.flat(trailing, prec::RETURN)),
1822                    }
1823                }
1824                out
1825            }
1826            ExprKind::Unary { op, operand } => {
1827                format!("{}{}", unary_symbol(*op), self.flat(operand, prec::UNARY))
1828            }
1829            ExprKind::Binary { op, lhs, rhs } => {
1830                let level = binary_prec(*op);
1831                format!(
1832                    "{} {} {}",
1833                    self.flat(lhs, level),
1834                    binary_symbol(*op),
1835                    self.flat(rhs, level + 1)
1836                )
1837            }
1838            ExprKind::Assign { op, target, value } => {
1839                let operator = match op {
1840                    Some(op) => format!("{}=", binary_symbol(*op)),
1841                    None => "=".to_string(),
1842                };
1843                format!(
1844                    "{} {operator} {}",
1845                    self.flat(target, prec::POSTFIX),
1846                    self.flat(value, prec::ASSIGN)
1847                )
1848            }
1849            ExprKind::Try(inner) => match &inner.kind {
1850                ExprKind::Await(awaited) => {
1851                    format!("await {}?", self.flat(awaited, prec::POSTFIX))
1852                }
1853                _ => format!("{}?", self.flat(inner, prec::POSTFIX)),
1854            },
1855            ExprKind::Await(inner) => format!("await {}", self.flat(inner, prec::POSTFIX)),
1856            ExprKind::Block(block) => self.flat_block(block),
1857            ExprKind::If {
1858                condition,
1859                then_branch,
1860                else_branch,
1861            } => {
1862                let mut out = format!(
1863                    "if {} {}",
1864                    self.flat_header(condition),
1865                    self.flat_block(then_branch)
1866                );
1867                if let Some(else_branch) = else_branch {
1868                    out.push_str(" else ");
1869                    out.push_str(&self.flat(else_branch, prec::RETURN));
1870                }
1871                out
1872            }
1873            ExprKind::Match { scrutinee, arms } => {
1874                // Arms are comma-separated here: on one line a bare newline
1875                // cannot end an arm, and the parser accepts the comma.
1876                let arms: Vec<String> = arms
1877                    .iter()
1878                    .map(|arm| {
1879                        format!(
1880                            "{} => {}",
1881                            self.pattern_flat(&arm.pattern),
1882                            self.flat(&arm.body, prec::RETURN)
1883                        )
1884                    })
1885                    .collect();
1886                if arms.is_empty() {
1887                    format!("match {} {{ }}", self.flat_header(scrutinee))
1888                } else {
1889                    format!(
1890                        "match {} {{ {} }}",
1891                        self.flat_header(scrutinee),
1892                        arms.join(", ")
1893                    )
1894                }
1895            }
1896            ExprKind::For {
1897                binding,
1898                iterable,
1899                body,
1900            } => format!(
1901                "for {} in {} {}",
1902                binding.node,
1903                self.flat_header(iterable),
1904                self.flat_block(body)
1905            ),
1906            ExprKind::While { condition, body } => format!(
1907                "while {} {}",
1908                self.flat_header(condition),
1909                self.flat_block(body)
1910            ),
1911            ExprKind::Scope { name, body } => {
1912                format!("scope {} {}", name.node, self.flat_block(body))
1913            }
1914            ExprKind::Return(value) => match value {
1915                Some(value) => format!("return {}", self.flat(value, prec::RETURN)),
1916                None => "return".to_string(),
1917            },
1918            ExprKind::Break(value) => match value {
1919                Some(value) => format!("break {}", self.flat(value, prec::RETURN)),
1920                None => "break".to_string(),
1921            },
1922            ExprKind::Continue => "continue".to_string(),
1923            ExprKind::Lambda {
1924                is_async,
1925                params,
1926                body,
1927            } => {
1928                let mut out = String::new();
1929                if *is_async {
1930                    out.push_str("async ");
1931                }
1932                out.push_str("fn(");
1933                let entries: Vec<String> = params.iter().map(|p| self.param_flat(p)).collect();
1934                out.push_str(&entries.join(", "));
1935                out.push_str(") ");
1936                out.push_str(&self.flat_block(body));
1937                out
1938            }
1939            ExprKind::Range {
1940                start,
1941                end,
1942                inclusive_end,
1943            } => format!(
1944                "{}{}{}",
1945                self.flat(start, prec::ADDITIVE),
1946                if *inclusive_end { ".." } else { "..<" },
1947                self.flat(end, prec::ADDITIVE)
1948            ),
1949        }
1950    }
1951
1952    fn args_flat(&self, args: &[Arg]) -> String {
1953        let entries: Vec<String> = args.iter().map(|arg| self.arg_flat(arg)).collect();
1954        entries.join(", ")
1955    }
1956
1957    fn arg_flat(&self, arg: &Arg) -> String {
1958        format!(
1959            "{}{}",
1960            self.arg_prefix_text(arg),
1961            self.flat(&arg.value, prec::RETURN)
1962        )
1963    }
1964
1965    fn flat_block(&self, block: &Block) -> String {
1966        if block_is_empty(block) {
1967            return "{ }".to_string();
1968        }
1969        let mut parts: Vec<String> = block
1970            .statements
1971            .iter()
1972            .map(|stmt| self.stmt_flat(stmt))
1973            .collect();
1974        if let Some(tail) = &block.tail {
1975            parts.push(self.flat(tail, prec::RETURN));
1976        }
1977        format!("{{ {} }}", parts.join(" "))
1978    }
1979
1980    fn stmt_flat(&self, stmt: &Stmt) -> String {
1981        match &stmt.kind {
1982            StmtKind::Expr(value) => self.flat(value, prec::RETURN),
1983            StmtKind::Let {
1984                is_var,
1985                name,
1986                ty,
1987                value,
1988            } => {
1989                let mut out = String::from(if *is_var { "var " } else { "let " });
1990                out.push_str(&name.node);
1991                if let Some(ty) = ty {
1992                    out.push_str(": ");
1993                    out.push_str(&ty.to_string());
1994                }
1995                out.push_str(" = ");
1996                out.push_str(&self.flat(value, prec::RETURN));
1997                out
1998            }
1999            StmtKind::Item(item) => {
2000                let mut sub = Formatter::new("");
2001                sub.item(item, 0);
2002                sub.finish()
2003                    .lines()
2004                    .map(str::trim)
2005                    .collect::<Vec<_>>()
2006                    .join(" ")
2007            }
2008        }
2009    }
2010
2011    /// Rebuilds a string literal from its parsed parts, for the rare case
2012    /// where the source spelling is unavailable.
2013    fn string_from_parts(&self, parts: &[StrPart]) -> String {
2014        let mut out = String::from("\"");
2015        for part in parts {
2016            match part {
2017                StrPart::Text(text) => {
2018                    for c in text.chars() {
2019                        match c {
2020                            '\\' => out.push_str("\\\\"),
2021                            '"' => out.push_str("\\\""),
2022                            '\n' => out.push_str("\\n"),
2023                            '\t' => out.push_str("\\t"),
2024                            '\r' => out.push_str("\\r"),
2025                            '\0' => out.push_str("\\0"),
2026                            '{' => out.push_str("\\{"),
2027                            '}' => out.push_str("\\}"),
2028                            c => out.push(c),
2029                        }
2030                    }
2031                }
2032                StrPart::Interpolation(value) => {
2033                    out.push('{');
2034                    out.push_str(&self.flat(value, prec::RETURN));
2035                    out.push('}');
2036                }
2037            }
2038        }
2039        out.push('"');
2040        out
2041    }
2042
2043    fn pattern_flat(&self, pattern: &Pattern) -> String {
2044        match &pattern.kind {
2045            PatternKind::Wildcard => "_".to_string(),
2046            PatternKind::Binding(name) => name.clone(),
2047            PatternKind::Literal(value) => self.flat(value, prec::RETURN),
2048            PatternKind::Variant { path, payload } => {
2049                let path: Vec<&str> = path.iter().map(|p| p.node.as_str()).collect();
2050                let mut out = path.join(".");
2051                if !payload.is_empty() {
2052                    let items: Vec<String> = payload.iter().map(|p| self.pattern_flat(p)).collect();
2053                    out.push('(');
2054                    out.push_str(&items.join(", "));
2055                    out.push(')');
2056                }
2057                out
2058            }
2059        }
2060    }
2061}
2062
2063#[cfg(test)]
2064mod tests {
2065    use super::*;
2066    use cove_diag::SourceMap;
2067    use std::path::{Path, PathBuf};
2068
2069    /// Test sources are written as raw strings that begin with a newline, so
2070    /// that the first line lines up with the rest in this file.
2071    fn src(text: &str) -> String {
2072        text.strip_prefix('\n').unwrap_or(text).to_string()
2073    }
2074
2075    fn parse(source: &str) -> SourceUnit {
2076        let mut sources = SourceMap::new();
2077        let file = sources.add("test.cove", source.to_string());
2078        match crate::parse_file(&sources, file) {
2079            Ok(unit) => unit,
2080            Err(diagnostics) => {
2081                let rendered: Vec<String> = diagnostics
2082                    .iter()
2083                    .map(|d| cove_diag::render(&sources, d))
2084                    .collect();
2085                panic!("source does not parse:\n{}", rendered.join(""));
2086            }
2087        }
2088    }
2089
2090    fn format(source: &str) -> String {
2091        format_source(source, &parse(source))
2092    }
2093
2094    /// Asserts that `source` is already formatted, which is how this module
2095    /// records the intended shape of a construct.
2096    fn formatted(source: &str) {
2097        let source = src(source);
2098        assert_eq!(format(&source), source, "\n--- source was:\n{source}");
2099    }
2100
2101    /// Asserts that `source` formats to `expected`, and that `expected` is a
2102    /// fixed point.
2103    fn reformats(source: &str, expected: &str) {
2104        let source = src(source);
2105        let expected = src(expected);
2106        assert_eq!(format(&source), expected, "\n--- source was:\n{source}");
2107        assert_eq!(format(&expected), expected, "formatting is not idempotent");
2108    }
2109
2110    /// The tree, with every span erased, so that two trees can be compared
2111    /// for the structure that formatting must preserve.
2112    fn without_spans(unit: &SourceUnit) -> String {
2113        let text = format!("{unit:?}");
2114        let mut out = String::new();
2115        let mut rest = text.as_str();
2116        while let Some(start) = rest.find("Span {") {
2117            out.push_str(&rest[..start]);
2118            out.push_str("Span");
2119            let close = rest[start..]
2120                .find('}')
2121                .expect("a Span renders as one set of braces");
2122            rest = &rest[start + close + 1..];
2123        }
2124        out.push_str(rest);
2125        out
2126    }
2127
2128    // -- items -------------------------------------------------------------
2129
2130    #[test]
2131    fn keeps_uses_in_source_order_above_one_blank_line() {
2132        reformats(
2133            "
2134use console
2135
2136use console.println
2137use http
2138
2139
2140/// A function.
2141fn a() { }
2142",
2143            "
2144use console
2145use console.println
2146use http
2147
2148/// A function.
2149fn a() { }
2150",
2151        );
2152    }
2153
2154    #[test]
2155    fn separates_top_level_items_with_one_blank_line() {
2156        reformats(
2157            "
2158fn a() { }
2159fn b() { }
2160
2161
2162
2163fn c() { }
2164",
2165            "
2166fn a() { }
2167
2168fn b() { }
2169
2170fn c() { }
2171",
2172        );
2173    }
2174
2175    #[test]
2176    fn formats_function_declarations() {
2177        formatted(
2178            "
2179/// Documented.
2180export async fn run<T>(self, var count: Int, items: String...) -> Result<T, U> {
2181  count
2182}
2183",
2184        );
2185    }
2186
2187    #[test]
2188    fn formats_a_test_declaration() {
2189        formatted(
2190            "
2191/// Greeting names the person it greets.
2192test fn greetsByName() -> Result<Unit, Error> {
2193  assert(greet(\"Ada\") == \"Hello, Ada!\")?
2194  Ok(())
2195}
2196",
2197        );
2198    }
2199
2200    #[test]
2201    fn formats_a_mutating_receiver_and_a_default_parameter() {
2202        formatted(
2203            "
2204impl Counter {
2205  /// Bumps.
2206  fn bump(var self, by: Int = 2 * 21) -> Int {
2207    self.hits
2208  }
2209}
2210",
2211        );
2212    }
2213
2214    #[test]
2215    fn formats_a_trait_with_docs_defaults_and_an_associated_function() {
2216        formatted(
2217            "
2218/// A value that can render itself for a human.
2219export trait Display {
2220  /// Returns the human-readable form.
2221  fn describe(self) -> String
2222
2223  /// Returns a short label.
2224  fn label(self) -> String {
2225    self.describe()
2226  }
2227
2228  /// Builds one from nothing.
2229  fn empty() -> Int
2230}
2231",
2232        );
2233    }
2234
2235    #[test]
2236    fn writes_an_empty_trait_on_one_line() {
2237        reformats(
2238            "
2239trait Marker {
2240}
2241",
2242            "
2243trait Marker { }
2244",
2245        );
2246    }
2247
2248    #[test]
2249    fn formats_a_conformance_and_keeps_it_apart_from_an_inherent_impl() {
2250        formatted(
2251            "
2252impl Display for Booking {
2253  fn describe(self) -> String {
2254    \"booking\"
2255  }
2256}
2257
2258impl Booking {
2259  /// The identifier.
2260  fn id(self) -> Int {
2261    1
2262  }
2263}
2264",
2265        );
2266    }
2267
2268    #[test]
2269    fn formats_bounds_on_type_parameters() {
2270        formatted(
2271            "
2272fn render<T: Display, U, V: Display + Ordered>(value: T, other: V) -> String {
2273  value.describe()
2274}
2275",
2276        );
2277    }
2278
2279    #[test]
2280    fn formats_dyn_types() {
2281        formatted(
2282            "
2283fn renderAll(values: Array<dyn Display>, one: dyn Display) -> dyn Display {
2284  one
2285}
2286",
2287        );
2288    }
2289
2290    #[test]
2291    fn keeps_comments_inside_a_trait() {
2292        formatted(
2293            "
2294trait Display {
2295  // Required.
2296  fn describe(self) -> String
2297
2298  /// Defaulted.
2299  fn label(self) -> String {
2300    // Falls back.
2301    self.describe()
2302  }
2303}
2304",
2305        );
2306    }
2307
2308    #[test]
2309    fn writes_struct_fields_one_per_line() {
2310        reformats(
2311            "
2312export struct Point { x: Int, y: Int }
2313
2314struct Tag(name: String, weight: Int)
2315
2316struct Empty { }
2317",
2318            "
2319export struct Point {
2320  x: Int
2321  y: Int
2322}
2323
2324struct Tag {
2325  name: String
2326  weight: Int
2327}
2328
2329struct Empty { }
2330",
2331        );
2332    }
2333
2334    /// The modifiers are written in one order whichever order they were
2335    /// read in, so an opaque export looks the same everywhere it appears.
2336    #[test]
2337    fn writes_the_opaque_modifier_after_export() {
2338        reformats(
2339            "
2340opaque export struct User { id: Int, name: String }
2341",
2342            "
2343export opaque struct User {
2344  id: Int
2345  name: String
2346}
2347",
2348        );
2349    }
2350
2351    #[test]
2352    fn writes_enum_cases_one_per_line_with_their_docs() {
2353        reformats(
2354            "
2355export enum Status { Pending, Active(Int), Failed(String, Int) }
2356",
2357            "
2358export enum Status {
2359  Pending
2360  Active(Int)
2361  Failed(String, Int)
2362}
2363",
2364        );
2365    }
2366
2367    #[test]
2368    fn formats_documented_fields_and_cases() {
2369        formatted(
2370            "
2371struct Config {
2372  /// The port.
2373  port: Int
2374}
2375
2376enum Level {
2377  /// The quiet one.
2378  Debug
2379}
2380",
2381        );
2382    }
2383
2384    #[test]
2385    fn formats_impl_blocks_and_type_aliases() {
2386        formatted(
2387            "
2388export type Handler = async fn(request: http.Request) -> Result<Unit, Error>
2389
2390impl Metrics<T> {
2391  /// One.
2392  fn one(self) -> Int {
2393    1
2394  }
2395
2396  /// Two.
2397  export fn two() -> Int {
2398    2
2399  }
2400}
2401
2402impl Empty { }
2403",
2404        );
2405    }
2406
2407    #[test]
2408    fn keeps_a_multi_line_doc_comment_attached() {
2409        formatted(
2410            "
2411/// One line.
2412///
2413/// Another line.
2414fn documented() { }
2415",
2416        );
2417    }
2418
2419    #[test]
2420    fn removes_a_blank_line_between_a_doc_comment_and_its_declaration() {
2421        reformats(
2422            "
2423/// Documented.
2424
2425fn documented() { }
2426",
2427            "
2428/// Documented.
2429fn documented() { }
2430",
2431        );
2432    }
2433
2434    // -- types -------------------------------------------------------------
2435
2436    #[test]
2437    fn formats_every_type_form() {
2438        formatted(
2439            "
2440fn types(
2441  a: Int,
2442  b: Array<String>,
2443  c: Map<String, Array<Int>>,
2444  d: http.Request,
2445  e: (),
2446  f: fn(String) -> Int,
2447  g: async fn(name: String, var items: Int...),
2448) { }
2449",
2450        );
2451    }
2452
2453    // -- statements --------------------------------------------------------
2454
2455    #[test]
2456    fn formats_statements() {
2457        formatted(
2458            "
2459fn statements() {
2460  let plain = 1
2461  let typed: Array<Int> = [1, 2]
2462  var mutable = 3
2463  mutable = 4
2464  mutable += 1
2465  mutable -= 1
2466  mutable *= 1
2467  mutable /= 1
2468  mutable %= 1
2469  mutable
2470}
2471",
2472        );
2473    }
2474
2475    #[test]
2476    fn keeps_one_blank_line_between_statements_and_none_at_a_block_edge() {
2477        reformats(
2478            "
2479fn spaced() {
2480
2481  let a = 1
2482
2483
2484
2485  let b = 2
2486
2487}
2488",
2489            "
2490fn spaced() {
2491  let a = 1
2492
2493  let b = 2
2494}
2495",
2496        );
2497    }
2498
2499    #[test]
2500    fn formats_a_nested_declaration_inside_a_block() {
2501        formatted(
2502            "
2503fn outer() {
2504  /// Inner.
2505  fn inner() -> Int {
2506    1
2507  }
2508  inner()
2509}
2510",
2511        );
2512    }
2513
2514    // -- expressions -------------------------------------------------------
2515
2516    /// A code-point literal survives formatting.
2517    ///
2518    /// It is an `Int` by the time the formatter sees it, so without
2519    /// `code_point_text` every one of these would come back as a number and
2520    /// the first `cove fmt` would delete the form from the tree. `' '` is
2521    /// here because the numeric predicate beside it refuses a span holding
2522    /// whitespace, and a space is a code point.
2523    #[test]
2524    fn formats_a_code_point_literal_and_keeps_its_spelling() {
2525        formatted(
2526            "
2527fn codePoints() {
2528  let letters = ['a', 'Z', '0']
2529  let punctuation = ['{', '}', ',', '\"']
2530  let escapes = ['\\n', '\\t', '\\r', '\\0', '\\\\', '\\'']
2531  let space = ' '
2532  let multibyte = '\u{e9}'
2533  space
2534}
2535",
2536        );
2537    }
2538
2539    #[test]
2540    fn formats_literals_and_keeps_their_spelling() {
2541        formatted(
2542            "
2543fn literals() {
2544  let ints = [1_000_000, 0xff, 0b1010]
2545  let floats = [1.5, 1_000.5, 1.5e3, 2e-2]
2546  let durations = [1ns, 500ms, 60s, 1h]
2547  let bools = [true, false]
2548  let unit = ()
2549  let text = \"escapes \\\\ \\\" \\n and {ints} interpolation\"
2550  text
2551}
2552",
2553        );
2554    }
2555
2556    #[test]
2557    fn formats_is_at_the_same_precedence_as_comparison() {
2558        formatted(
2559            "
2560fn identity(a: Vector<Int>, b: Vector<Int>) {
2561  a is b && a == b
2562}
2563",
2564        );
2565    }
2566
2567    #[test]
2568    fn formats_calls_labels_spreads_generics_and_trailing_closures() {
2569        formatted(
2570            "
2571fn calls(var output: Vector<Int>) {
2572  plain(1, 2)
2573  labeled(low: 1, high: 2)
2574  fill(var output)
2575  joinAll(\"-\", ...ready)
2576  api.fetch<Array<Booking>>(\"/bookings\")
2577  tasks.spawn { fetch() }
2578  clock.timeout(500ms) { fetch() }
2579  Point(x: 1, y: 2)
2580}
2581",
2582        );
2583    }
2584
2585    #[test]
2586    fn formats_lambdas_with_and_without_parameters() {
2587        formatted(
2588            "
2589fn lambdas() {
2590  let one = fn(n) {
2591    n * 2
2592  }
2593  let none = async fn() {
2594    one
2595  }
2596  none
2597}
2598",
2599        );
2600    }
2601
2602    #[test]
2603    fn parenthesises_only_where_precedence_requires_it() {
2604        reformats(
2605            "
2606fn precedence() {
2607  let a = (1 + 2) * 3
2608  let b = 1 + (2 * 3)
2609  let c = -(1 + 2)
2610  let d = !(a && b)
2611  let e = (a + b)?
2612  let f = (a + b).field
2613  let g = a || b && c
2614  let h = (a || b) && c
2615  let i = (0..<3).isEmpty()
2616  let j = 1 - (2 - 3)
2617  j
2618}
2619",
2620            "
2621fn precedence() {
2622  let a = (1 + 2) * 3
2623  let b = 1 + 2 * 3
2624  let c = -(1 + 2)
2625  let d = !(a && b)
2626  let e = (a + b)?
2627  let f = (a + b).field
2628  let g = a || b && c
2629  let h = (a || b) && c
2630  let i = (0..<3).isEmpty()
2631  let j = 1 - (2 - 3)
2632  j
2633}
2634",
2635        );
2636    }
2637
2638    #[test]
2639    fn writes_await_before_the_question_mark_it_propagates() {
2640        formatted(
2641            "
2642async fn awaiting() {
2643  await task()?
2644  await task()?.field
2645  await task()
2646}
2647",
2648        );
2649    }
2650
2651    #[test]
2652    fn formats_control_flow_across_lines() {
2653        formatted(
2654            "
2655fn control(items: Array<Int>) -> Int {
2656  if items.isEmpty() {
2657    0
2658  } else if items.length() == 1 {
2659    1
2660  } else {
2661    2
2662  }
2663
2664  for item in items {
2665    item
2666  }
2667
2668  for index in 0..<3 {
2669    index
2670  }
2671
2672  while items.isEmpty() {
2673    items
2674  }
2675
2676  scope tasks {
2677    tasks
2678  }
2679
2680  {
2681    let inner = 1
2682    inner
2683  }
2684
2685  return 1
2686}
2687",
2688        );
2689    }
2690
2691    /// A `break` or `return` alone on its line keeps the line to itself. The
2692    /// formatter used to write the statement under such a keyword back as its
2693    /// operand, because that is how the parser had read it, which turned a
2694    /// misreading into what the source said.
2695    #[test]
2696    fn keeps_a_bare_break_or_return_on_its_own_line() {
2697        formatted(
2698            "
2699fn stops(items: Array<Int>) -> Int {
2700  var seen = 0
2701  while true {
2702    seen += 1
2703    break
2704    seen = 99
2705  }
2706
2707  for item in items {
2708    break item
2709  }
2710
2711  return
2712  seen = 99
2713}
2714",
2715        );
2716    }
2717
2718    #[test]
2719    fn writes_match_arms_one_per_line() {
2720        reformats(
2721            "
2722fn matching(value: Card) -> String {
2723  match value {
2724    -1 => \"minus one\",
2725    \"yes\" => \"literal\",
2726    true => \"flag\",
2727    Card.Blank => \"blank\",
2728    Card.Numbered(count) => { let doubled = count * 2
2729      \"{doubled}\" }
2730    other => other,
2731    _ => \"wildcard\",
2732  }
2733}
2734",
2735            "
2736fn matching(value: Card) -> String {
2737  match value {
2738    -1 => \"minus one\"
2739    \"yes\" => \"literal\"
2740    true => \"flag\"
2741    Card.Blank => \"blank\"
2742    Card.Numbered(count) => {
2743      let doubled = count * 2
2744      \"{doubled}\"
2745    }
2746    other => other
2747    _ => \"wildcard\"
2748  }
2749}
2750",
2751        );
2752    }
2753
2754    #[test]
2755    fn parenthesises_a_header_that_would_otherwise_end_in_a_brace() {
2756        let source = src("
2757fn headers() {
2758  if (tasks.spawn { ready() }) {
2759    1
2760  }
2761}
2762");
2763        assert_eq!(format(&source), source);
2764        assert_eq!(
2765            without_spans(&parse(&source)),
2766            without_spans(&parse(&format(&source)))
2767        );
2768    }
2769
2770    #[test]
2771    fn writes_an_empty_block_on_one_line() {
2772        formatted(
2773            "
2774fn empty() {
2775  let nothing = { }
2776  nothing
2777}
2778",
2779        );
2780    }
2781
2782    // -- line breaking -----------------------------------------------------
2783
2784    #[test]
2785    fn breaks_an_argument_list_one_per_line_with_a_trailing_comma() {
2786        reformats(
2787            "
2788fn wide() {
2789  configure(alphaValueHere, betaValueHere, gammaValueHere, deltaValueHere, epsilon)
2790}
2791",
2792            "
2793fn wide() {
2794  configure(
2795    alphaValueHere,
2796    betaValueHere,
2797    gammaValueHere,
2798    deltaValueHere,
2799    epsilon,
2800  )
2801}
2802",
2803        );
2804    }
2805
2806    #[test]
2807    fn breaks_a_parameter_list_one_per_line_when_the_signature_is_too_wide() {
2808        reformats(
2809            "
2810fn wideSignature(alphaValue: String, betaValue: String, gammaValue: String) -> Int {
2811  1
2812}
2813",
2814            "
2815fn wideSignature(
2816  alphaValue: String,
2817  betaValue: String,
2818  gammaValue: String,
2819) -> Int {
2820  1
2821}
2822",
2823        );
2824    }
2825
2826    #[test]
2827    fn breaks_an_array_literal_one_element_per_line() {
2828        reformats(
2829            "
2830fn wideArray() {
2831  let items = [alphaValueHere, betaValueHere, gammaValueHere, deltaValueHere, eps]
2832  items
2833}
2834",
2835            "
2836fn wideArray() {
2837  let items = [
2838    alphaValueHere,
2839    betaValueHere,
2840    gammaValueHere,
2841    deltaValueHere,
2842    eps,
2843  ]
2844  items
2845}
2846",
2847        );
2848    }
2849
2850    #[test]
2851    fn breaks_a_binary_expression_after_its_operator() {
2852        reformats(
2853            "
2854fn wideSum() {
2855  let total = alphaValueHere + betaValueHere + gammaValueHere + deltaValueHereOne
2856  total
2857}
2858",
2859            "
2860fn wideSum() {
2861  let total = alphaValueHere + betaValueHere + gammaValueHere +
2862    deltaValueHereOne
2863  total
2864}
2865",
2866        );
2867    }
2868
2869    #[test]
2870    fn breaks_a_method_chain_before_each_dot() {
2871        reformats(
2872            "
2873fn wideChain(reading: Reading) {
2874  reading.normalise().withPrecision(4).formattedAsText().withoutTrailingZeroesAt()
2875}
2876",
2877            "
2878fn wideChain(reading: Reading) {
2879  reading.normalise()
2880    .withPrecision(4)
2881    .formattedAsText()
2882    .withoutTrailingZeroesAt()
2883}
2884",
2885        );
2886    }
2887
2888    #[test]
2889    fn breaks_an_argument_list_above_a_trailing_closure() {
2890        reformats(
2891            "
2892fn wideTrailing() {
2893  clock.timeout(alphaValueHere, betaValueHere, gammaValueHere, deltaValueHereOk) { work() }
2894  1
2895}
2896",
2897            "
2898fn wideTrailing() {
2899  clock.timeout(
2900    alphaValueHere,
2901    betaValueHere,
2902    gammaValueHere,
2903    deltaValueHereOk,
2904  ) {
2905    work()
2906  }
2907  1
2908}
2909",
2910        );
2911    }
2912
2913    #[test]
2914    fn keeps_a_call_head_on_one_line_and_expands_a_closure_below_it() {
2915        formatted(
2916            "
2917fn callbacks(app: App) {
2918  builder.get(\"/health\", withObservability(app, async fn(request) {
2919    Ok(request)
2920  }))
2921}
2922",
2923        );
2924    }
2925
2926    #[test]
2927    fn expands_a_sole_initializer_argument_in_place() {
2928        formatted(
2929            "
2930fn initializers(builder: RouterBuilder, path: String, handler: Handler) {
2931  builder.routes.push(Route(
2932    method: http.Method.Get,
2933    path: path,
2934    handler: handler,
2935  ))
2936}
2937",
2938        );
2939    }
2940
2941    #[test]
2942    fn leaves_a_lone_unbreakable_argument_where_it_is() {
2943        // Breaking it would produce a line that is still too long.
2944        formatted(
2945            "
2946fn unbreakable() {
2947  println(\"a string literal so long that no line break anywhere can ever help\")?
2948}
2949",
2950        );
2951    }
2952
2953    #[test]
2954    fn breaks_a_function_type_that_does_not_fit() {
2955        reformats(
2956            "
2957export type Handler = async fn(request: http.Request, retries: Int) -> Result<http.Response, Error>
2958",
2959            "
2960export type Handler = async fn(
2961  request: http.Request,
2962  retries: Int,
2963) -> Result<http.Response, Error>
2964",
2965        );
2966    }
2967
2968    #[test]
2969    fn every_break_it_introduces_still_parses_the_same_way() {
2970        let sources = [
2971            "fn a() {\n  configure(alphaValueHere, betaValueHere, gammaValueHere, deltaValueHere, epsilon)\n}\n",
2972            "fn a() {\n  let total = alphaValueHere + betaValueHere + gammaValueHere + deltaValueHereOne\n}\n",
2973            "fn a(reading: R) {\n  reading.normalise().withPrecision(4).formattedAsText().withoutTrailingZeroesAt()\n}\n",
2974            "fn a() {\n  let items = [alphaValueHere, betaValueHere, gammaValueHere, deltaValueHere, eps]\n}\n",
2975            "fn wideSignature(alphaValue: String, betaValue: String, gammaValue: String) -> Int {\n  1\n}\n",
2976            "fn a() {\n  builder.get(\"/health\", withObservability(app, async fn(request) { Ok(1) }))\n}\n",
2977            "fn a() {\n  clock.timeout(alphaValueHere, betaValueHere, gammaValueHere, deltaValue) { w() }\n  1\n}\n",
2978        ];
2979        for source in sources {
2980            let formatted = format(source);
2981            assert_ne!(formatted, source, "this case is meant to be reformatted");
2982            assert_eq!(
2983                without_spans(&parse(source)),
2984                without_spans(&parse(&formatted)),
2985                "reformatting changed the tree of:\n{source}\ninto:\n{formatted}"
2986            );
2987        }
2988    }
2989
2990    // -- comments ----------------------------------------------------------
2991
2992    #[test]
2993    fn keeps_a_comment_on_its_own_line_above_what_follows_it() {
2994        formatted(
2995            "
2996// Above the use.
2997use console.println
2998
2999// Above the item.
3000fn commented() {
3001  // Above the statement.
3002  let a = 1
3003
3004  // After a blank line.
3005  a
3006}
3007",
3008        );
3009    }
3010
3011    #[test]
3012    fn keeps_a_comment_at_the_end_of_the_line_it_was_written_on() {
3013        formatted(
3014            "
3015fn trailing() {
3016  let a = 1 // one
3017  a
3018}
3019",
3020        );
3021    }
3022
3023    #[test]
3024    fn aligns_a_run_of_trailing_comments() {
3025        reformats(
3026            "
3027fn aligned() {
3028  let a = 1 // one
3029  let longerName = 2 // two
3030  let c = 3 // three
3031
3032  let d = 4 // alone
3033  d
3034}
3035",
3036            "
3037fn aligned() {
3038  let a = 1          // one
3039  let longerName = 2 // two
3040  let c = 3          // three
3041
3042  let d = 4 // alone
3043  d
3044}
3045",
3046        );
3047    }
3048
3049    #[test]
3050    fn keeps_a_comment_before_a_closing_brace() {
3051        formatted(
3052            "
3053fn closing() {
3054  let a = 1
3055  // The last word.
3056}
3057",
3058        );
3059    }
3060
3061    #[test]
3062    fn keeps_a_comment_at_the_end_of_the_file() {
3063        formatted(
3064            "
3065fn done() { }
3066
3067// A closing remark
3068// over two lines.
3069",
3070        );
3071    }
3072
3073    #[test]
3074    fn keeps_a_block_comment_and_re_indents_its_continuation_lines() {
3075        reformats(
3076            "
3077fn blocks() {
3078        /* one
3079           two */
3080  let a = 1 /* trailing */
3081  a
3082}
3083",
3084            "
3085fn blocks() {
3086  /* one
3087     two */
3088  let a = 1 /* trailing */
3089  a
3090}
3091",
3092        );
3093    }
3094
3095    #[test]
3096    fn keeps_a_comment_inside_an_argument_list_by_breaking_the_call() {
3097        formatted(
3098            "
3099fn inside() {
3100  configure(
3101    // Why alpha.
3102    alpha,
3103    beta, // and beta
3104  )
3105}
3106",
3107        );
3108    }
3109
3110    #[test]
3111    fn keeps_a_comment_between_enum_cases_and_struct_fields() {
3112        formatted(
3113            "
3114struct Point {
3115  // The horizontal one.
3116  x: Int
3117  y: Int // the vertical one
3118}
3119
3120enum Level {
3121  // The quiet one.
3122  Debug
3123  Info
3124}
3125",
3126        );
3127    }
3128
3129    #[test]
3130    fn never_drops_a_comment() {
3131        let source = src("
3132// one
3133use console.println // two
3134
3135// three
3136fn a(/* four */ x: Int) { // five
3137  // six
3138  let y = x // seven
3139  /* eight */
3140  y
3141  // nine
3142}
3143// ten
3144");
3145        let output = format(&source);
3146        for word in [
3147            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
3148        ] {
3149            assert!(
3150                output.contains(word),
3151                "comment `{word}` was dropped:\n{output}"
3152            );
3153        }
3154        assert_eq!(format(&output), output, "formatting is not idempotent");
3155    }
3156
3157    // -- whole-repository properties ---------------------------------------
3158
3159    fn repo_root() -> PathBuf {
3160        Path::new(env!("CARGO_MANIFEST_DIR"))
3161            .join("..")
3162            .join("..")
3163            .canonicalize()
3164            .expect("the workspace root exists")
3165    }
3166
3167    fn cove_files() -> Vec<PathBuf> {
3168        fn walk(dir: &Path, found: &mut Vec<PathBuf>) {
3169            let Ok(entries) = std::fs::read_dir(dir) else {
3170                return;
3171            };
3172            let mut paths: Vec<PathBuf> =
3173                entries.filter_map(|e| e.ok().map(|e| e.path())).collect();
3174            paths.sort();
3175            for path in paths {
3176                let name = path.file_name().unwrap_or_default().to_string_lossy();
3177                if name.starts_with('.') || name == "target" {
3178                    continue;
3179                }
3180                if path.is_dir() {
3181                    walk(&path, found);
3182                } else if path.extension().and_then(|e| e.to_str()) == Some("cove") {
3183                    found.push(path);
3184                }
3185            }
3186        }
3187        let mut found = Vec::new();
3188        walk(&repo_root(), &mut found);
3189        assert!(!found.is_empty(), "the repository has `.cove` files");
3190
3191        // The end-to-end suite deliberately contains sources that do not
3192        // parse, because it pins the diagnostics they produce. `cove fmt`
3193        // never rewrites a file it cannot parse, so neither do these tests.
3194        let mut formattable = Vec::new();
3195        let mut unparsable = Vec::new();
3196        for path in found {
3197            let Ok(source) = std::fs::read_to_string(&path) else {
3198                continue;
3199            };
3200            let mut sources = SourceMap::new();
3201            let file = sources.add(path.clone(), source);
3202            if crate::parse_file(&sources, file).is_ok() {
3203                formattable.push(path);
3204            } else {
3205                unparsable.push(path);
3206            }
3207        }
3208        assert!(
3209            formattable.len() > unparsable.len() * 4,
3210            "most of the repository should parse; {} of {} did not",
3211            unparsable.len(),
3212            formattable.len() + unparsable.len()
3213        );
3214        formattable
3215    }
3216
3217    #[test]
3218    fn formatting_every_repository_file_twice_changes_nothing() {
3219        for path in cove_files() {
3220            let source = std::fs::read_to_string(&path).expect("the file is readable");
3221            let once = format(&source);
3222            let twice = format(&once);
3223            assert_eq!(once, twice, "`{}` is not a fixed point", path.display());
3224        }
3225    }
3226
3227    #[test]
3228    fn formatting_every_repository_file_preserves_its_tree() {
3229        for path in cove_files() {
3230            let source = std::fs::read_to_string(&path).expect("the file is readable");
3231            let formatted = format(&source);
3232            assert_eq!(
3233                without_spans(&parse(&source)),
3234                without_spans(&parse(&formatted)),
3235                "formatting `{}` changed its tree",
3236                path.display()
3237            );
3238        }
3239    }
3240
3241    #[test]
3242    fn every_repository_file_formats_to_clean_layout() {
3243        for path in cove_files() {
3244            let source = std::fs::read_to_string(&path).expect("the file is readable");
3245            let formatted = format(&source);
3246            assert!(
3247                formatted.ends_with('\n') && !formatted.ends_with("\n\n"),
3248                "`{}` does not end in exactly one newline",
3249                path.display()
3250            );
3251            for (i, line) in formatted.lines().enumerate() {
3252                assert!(
3253                    !line.contains('\t'),
3254                    "`{}` line {} contains a tab",
3255                    path.display(),
3256                    i + 1
3257                );
3258                assert_eq!(
3259                    line.trim_end(),
3260                    line,
3261                    "`{}` line {} has trailing whitespace",
3262                    path.display(),
3263                    i + 1
3264                );
3265            }
3266        }
3267    }
3268
3269    #[test]
3270    fn formatting_every_repository_file_keeps_every_comment() {
3271        for path in cove_files() {
3272            let source = std::fs::read_to_string(&path).expect("the file is readable");
3273            let formatted = format(&source);
3274            let before = scan_comments(&source);
3275            let after = scan_comments(&formatted);
3276            assert_eq!(
3277                before.len(),
3278                after.len(),
3279                "formatting `{}` changed how many comments it has",
3280                path.display()
3281            );
3282        }
3283    }
3284
3285    #[test]
3286    fn format_unit_formats_a_tree_without_its_source() {
3287        let unit = parse("fn a() {\n  let x = 0xff\n  x\n}\n");
3288        assert_eq!(format_unit(&unit), "fn a() {\n  let x = 255\n  x\n}\n");
3289    }
3290}