Skip to main content

wolf_fmt/
lib.rs

1//! `wolf fmt`: the canonical layout of wolfpack source (spec chapter 3, section 8a).
2//!
3//! The formatter parses a file, builds a [`doc::Doc`] from the syntax tree and prints it within
4//! 100 columns. Literals, doc comments and ordinary comments are copied from the source text. The
5//! result is checked before it is returned: it must parse to the same tree and keep every comment,
6//! so a bug in the formatter never changes a program.
7
8mod doc;
9
10use wolf_ast::{
11    self as ast, Arg, Arm, AssignOp, BinaryOp, Block, ExprKind, ExternDeclKind, File, ForIter,
12    GenericParam, ItemKind, Mode, ParamKind, Pat, PatKind, SectionKind, Stmt, StmtKind, Ty, TyKind,
13    UnaryOp, VariantPayload,
14};
15use wolf_diag::Diagnostic;
16use wolf_span::{FileId, Span};
17
18use crate::doc::{Doc, INDENT, WIDTH, cat, flat, flat_width, group, has_hard, nest, text};
19
20/// Why a file was not formatted.
21#[derive(Debug)]
22pub enum FmtError {
23    /// The file has syntax errors; they are returned as they are.
24    Syntax(Vec<Diagnostic>),
25    /// The formatter produced a different program; this is a bug of the formatter.
26    Internal(String),
27}
28
29/// Formats one source file.
30pub fn format(file: FileId, source: &str) -> Result<String, FmtError> {
31    let lexed = wolf_lexer::lex(file, source);
32    let parsed = wolf_parser::parse(file, source, &lexed.tokens);
33    let mut diagnostics = lexed.diagnostics;
34    diagnostics.extend(parsed.diagnostics);
35    if !diagnostics.is_empty() {
36        return Err(FmtError::Syntax(diagnostics));
37    }
38    let comments: Vec<(u32, u32)> = lexed
39        .comments
40        .iter()
41        .map(|c| (c.span.start, c.span.end))
42        .collect();
43    let mut f = Fmt {
44        src: source,
45        comments: comments.clone(),
46        next: 0,
47        depth: 0,
48    };
49    let d = f.file(&parsed.file);
50    let mut out = doc::print(&d);
51    while out.ends_with('\n') || out.ends_with(' ') {
52        out.pop();
53    }
54    out.push('\n');
55    check(file, source, &parsed.file, &comments, &out)?;
56    Ok(out)
57}
58
59/// The formatted text must be the same program with the same comments.
60fn check(
61    file: FileId,
62    source: &str,
63    tree: &File,
64    comments: &[(u32, u32)],
65    out: &str,
66) -> Result<(), FmtError> {
67    let lexed = wolf_lexer::lex(file, out);
68    let parsed = wolf_parser::parse(file, out, &lexed.tokens);
69    if !lexed.diagnostics.is_empty() || !parsed.diagnostics.is_empty() {
70        return Err(FmtError::Internal(
71            "the formatted text has syntax errors".to_string(),
72        ));
73    }
74    if ast::dump(&parsed.file) != ast::dump(tree) {
75        return Err(FmtError::Internal(
76            "the formatted text has a different syntax tree".to_string(),
77        ));
78    }
79    let text_of = |s: &str, (a, b): (u32, u32)| {
80        s.get(a as usize..b as usize)
81            .unwrap_or("")
82            .trim_end()
83            .to_string()
84    };
85    let before: Vec<String> = comments.iter().map(|c| text_of(source, *c)).collect();
86    let after: Vec<String> = lexed
87        .comments
88        .iter()
89        .map(|c| text_of(out, (c.span.start, c.span.end)))
90        .collect();
91    if before != after {
92        return Err(FmtError::Internal(
93            "the formatted text lost or reordered a comment".to_string(),
94        ));
95    }
96    Ok(())
97}
98
99struct Fmt<'a> {
100    src: &'a str,
101    /// Ordinary comments: start and end offsets, in source order.
102    comments: Vec<(u32, u32)>,
103    /// The first comment not printed yet.
104    next: usize,
105    /// The indentation level of the statement or member being built.
106    depth: usize,
107}
108
109/// Something printed one per line: its start (before its docs) and end in the source, and how
110/// to build it. It is built after the comments before it are printed, so that a nested element
111/// never takes a comment that belongs before the outer one.
112struct Element<'b, 'a> {
113    start: u32,
114    end: u32,
115    build: Box<dyn FnOnce(&mut Fmt<'a>) -> Doc + 'b>,
116}
117
118impl<'a> Fmt<'a> {
119    fn src(&self, span: Span) -> &str {
120        self.src
121            .get(span.start as usize..span.end as usize)
122            .unwrap_or("")
123    }
124
125    fn between(&self, a: u32, b: u32) -> &str {
126        if a >= b {
127            return "";
128        }
129        self.src.get(a as usize..b as usize).unwrap_or("")
130    }
131
132    /// Whether the source has a blank line between `a` and `b`.
133    fn blank_between(&self, a: u32, b: u32) -> bool {
134        let gap = self.between(a, b);
135        let mut newlines = 0;
136        for c in gap.chars() {
137            if c == '\n' {
138                newlines += 1;
139                if newlines >= 2 {
140                    return true;
141                }
142            } else if !c.is_whitespace() {
143                newlines = 0;
144            }
145        }
146        false
147    }
148
149    fn comment_text(&self, (a, b): (u32, u32)) -> String {
150        self.between(a, b).trim_end().to_string()
151    }
152
153    /// Whether a comment starts inside the range.
154    fn comment_inside(&self, a: u32, b: u32) -> bool {
155        self.comments
156            .get(self.next..)
157            .unwrap_or(&[])
158            .iter()
159            .any(|(s, _)| *s >= a && *s < b)
160    }
161
162    /// The comments before `pos` that are not printed yet, each on its own line.
163    fn leading(&mut self, pos: u32, prev_end: Option<u32>) -> Vec<Doc> {
164        let mut out = Vec::new();
165        let mut last = prev_end;
166        while let Some(&(s, e)) = self.comments.get(self.next) {
167            if s >= pos {
168                break;
169            }
170            if let Some(p) = last {
171                if self.blank_between(p, s) && !out.is_empty() {
172                    out.push(Doc::Hard);
173                }
174            }
175            out.push(text(self.comment_text((s, e))));
176            out.push(Doc::Hard);
177            last = Some(e);
178            self.next += 1;
179        }
180        if let Some(p) = last {
181            if !out.is_empty() && self.blank_between(p, pos) {
182                out.push(Doc::Hard);
183            }
184        }
185        out
186    }
187
188    /// A comment after `end` on the same source line.
189    fn trailing(&mut self, end: u32) -> Option<Doc> {
190        let &(s, e) = self.comments.get(self.next)?;
191        if s < end || self.between(end, s).contains('\n') {
192            return None;
193        }
194        self.next += 1;
195        Some(text(format!(" {}", self.comment_text((s, e)))))
196    }
197
198    /// Elements one per line inside braces that close at `close`, with comments and blank lines
199    /// kept. The result starts with a newline and is nested; the caller adds the closing line.
200    fn lines(&mut self, elements: Vec<Element<'_, 'a>>, close: u32) -> Doc {
201        let mut parts = Vec::new();
202        let mut prev: Option<u32> = None;
203        for el in elements {
204            let blank = prev.is_some_and(|p| self.blank_between(p, self.first_before(el.start)));
205            let lead = self.leading(el.start, None);
206            parts.push(Doc::Hard);
207            if blank {
208                parts.push(Doc::Hard);
209            }
210            parts.extend(lead);
211            parts.push((el.build)(self));
212            if let Some(t) = self.trailing(el.end) {
213                parts.push(t);
214            }
215            prev = Some(el.end);
216        }
217        let blank = prev.is_some_and(|p| self.blank_between(p, self.first_before(close)));
218        let mut rest = self.leading(close, None);
219        if !rest.is_empty() {
220            parts.push(Doc::Hard);
221            if blank {
222                parts.push(Doc::Hard);
223            }
224            // The closing line follows; no newline after the last comment.
225            while matches!(rest.last(), Some(Doc::Hard)) {
226                rest.pop();
227            }
228            parts.extend(rest);
229        }
230        nest(cat(parts))
231    }
232
233    /// The start of the first comment not printed yet if it is before `pos`, else `pos`.
234    fn first_before(&self, pos: u32) -> u32 {
235        self.comments
236            .get(self.next)
237            .map(|c| c.0)
238            .filter(|s| *s < pos)
239            .unwrap_or(pos)
240    }
241
242    // ----- file and sections ----------------------------------------------------------------
243
244    fn file(&mut self, file: &File) -> Doc {
245        let mut parts = Vec::new();
246        let mut prev: Option<u32> = None;
247        for d in &file.docs {
248            parts.extend(self.leading(d.span.start, prev));
249            if prev.is_some_and(|p| self.blank_between(p, d.span.start)) {
250                parts.push(Doc::Hard);
251            }
252            parts.push(text(self.src(d.span).trim_end()));
253            parts.push(Doc::Hard);
254            prev = Some(d.span.end);
255        }
256        if let Some(m) = &file.module {
257            parts.extend(self.leading(m.span.start, prev));
258            if prev.is_some_and(|p| self.blank_between(p, m.span.start)) {
259                parts.push(Doc::Hard);
260            }
261            parts.push(text(format!("module {}", path(m))));
262            if let Some(t) = self.trailing(m.span.end) {
263                parts.push(t);
264            }
265            parts.push(Doc::Hard);
266            prev = Some(m.span.end);
267        }
268        for s in &file.sections {
269            if prev.is_some() {
270                parts.push(Doc::Hard);
271            }
272            parts.extend(self.leading(s.name_span.start, None));
273            if let (SectionKind::Main, Some(ItemKind::Fn(decl))) =
274                (s.kind, s.items.first().map(|i| &i.kind))
275            {
276                parts.push(self.main_header(&decl.sig));
277                if let Some(b) = &decl.body {
278                    parts.push(text(" "));
279                    self.depth = 0;
280                    parts.push(self.block(b, true));
281                }
282                if let Some(t) = self.trailing(s.span.end) {
283                    parts.push(t);
284                }
285                parts.push(Doc::Hard);
286                prev = Some(s.span.end);
287                continue;
288            }
289            parts.push(text(format!("{} {{", s.kind.name())));
290            self.depth = 1;
291            let elements: Vec<Element> = s.items.iter().map(Self::item).collect();
292            parts.push(self.lines(elements, s.span.end.saturating_sub(1)));
293            self.depth = 0;
294            parts.push(Doc::Hard);
295            parts.push(text("}"));
296            if let Some(t) = self.trailing(s.span.end) {
297                parts.push(t);
298            }
299            parts.push(Doc::Hard);
300            prev = Some(s.span.end);
301        }
302        let rest = self.leading(u32::MAX, prev);
303        if !rest.is_empty() {
304            parts.push(Doc::Hard);
305            parts.extend(rest);
306        }
307        cat(parts)
308    }
309
310    fn docs(&self, docs: &[ast::Doc]) -> Vec<Doc> {
311        let mut out = Vec::new();
312        for d in docs {
313            out.push(text(self.src(d.span).trim_end()));
314            out.push(Doc::Hard);
315        }
316        out
317    }
318
319    fn element<'b>(
320        docs: &'b [ast::Doc],
321        span: Span,
322        build: impl FnOnce(&mut Fmt<'a>) -> Doc + 'b,
323    ) -> Element<'b, 'a> {
324        let start = docs
325            .first()
326            .map_or(span.start, |d| d.span.start.min(span.start));
327        Element {
328            start,
329            end: span.end,
330            build: Box::new(move |f| {
331                let mut parts = f.docs(docs);
332                parts.push(build(f));
333                cat(parts)
334            }),
335        }
336    }
337
338    // ----- items ------------------------------------------------------------------------------
339
340    fn item<'b>(item: &'b ast::Item) -> Element<'b, 'a> {
341        Self::element(&item.docs, item.span, move |f| f.item_doc(item))
342    }
343
344    fn item_doc(&mut self, item: &ast::Item) -> Doc {
345        match &item.kind {
346            ItemKind::Import(i) => {
347                let mut s = format!("{}{}", vis(i.public), path(&i.path));
348                if let Some(a) = &i.alias {
349                    s.push_str(&format!(" as {}", a.name));
350                }
351                let mut parts = vec![text(s)];
352                if let Some(names) = &i.names {
353                    let items: Vec<Doc> = names
354                        .iter()
355                        .map(|n| match &n.alias {
356                            Some(a) => text(format!("{} as {}", n.name.name, a.name)),
357                            None => text(n.name.name.clone()),
358                        })
359                        .collect();
360                    parts.push(delimited(".{", "}", items));
361                }
362                cat(parts)
363            }
364            ItemKind::ExternTarget(t) => {
365                let close = item.span.end.saturating_sub(1);
366                let elements = t
367                    .decls
368                    .iter()
369                    .map(|d| {
370                        Self::element(&d.docs, d.span, move |f| {
371                            let head = match &d.kind {
372                                ExternDeclKind::Fn(sig) => {
373                                    cat(vec![text(vis(d.public)), f.sig(sig)])
374                                }
375                                ExternDeclKind::Type(n) => {
376                                    text(format!("{}type {}", vis(d.public), n.name))
377                                }
378                                ExternDeclKind::Struct(n) => text(format!("struct {}", n.name)),
379                            };
380                            cat(vec![head, text(format!(" from {}", f.src(d.from.span)))])
381                        })
382                    })
383                    .collect();
384                let inner = self.nested(elements, close);
385                braces(text(format!("{} {{", t.target.name)), inner)
386            }
387            ItemKind::Config(c) => {
388                let head = format!("{}{}: ", vis(c.public), c.name.name);
389                let head = cat(vec![text(head), self.ty(&c.ty), text(" = ")]);
390                self.assigned(head, &c.value)
391            }
392            ItemKind::Struct(s) => {
393                let head = text(format!(
394                    "{}struct {}{} {{",
395                    vis(s.public),
396                    s.name.name,
397                    self.generics(&s.generics)
398                ));
399                if s.fields.is_empty() {
400                    text(format!(
401                        "{}struct {}{} {{}}",
402                        vis(s.public),
403                        s.name.name,
404                        self.generics(&s.generics)
405                    ))
406                } else {
407                    let close = item.span.end.saturating_sub(1);
408                    let inner = self.nested(Self::fields(&s.fields), close);
409                    braces(head, inner)
410                }
411            }
412            ItemKind::Enum(e) => {
413                let head = text(format!(
414                    "{}enum {}{} {{",
415                    vis(e.public),
416                    e.name.name,
417                    self.generics(&e.generics)
418                ));
419                let close = item.span.end.saturating_sub(1);
420                let elements = e
421                    .variants
422                    .iter()
423                    .map(|v| Self::element(&v.docs, v.span, move |f| f.variant(v)))
424                    .collect();
425                let inner = self.nested(elements, close);
426                braces(head, inner)
427            }
428            ItemKind::Alias(a) => cat(vec![
429                text(format!(
430                    "{}type {}{} = ",
431                    vis(a.public),
432                    a.name.name,
433                    self.generics(&a.generics)
434                )),
435                self.ty(&a.ty),
436            ]),
437            ItemKind::IntrinsicType(t) => text(format!(
438                "{}intrinsic type {}{}",
439                vis(t.public),
440                t.name.name,
441                self.generics(&t.generics)
442            )),
443            ItemKind::Interface(i) => {
444                let head = text(format!(
445                    "{}interface {}{} {{",
446                    vis(i.public),
447                    i.name.name,
448                    self.generics(&i.generics)
449                ));
450                let close = item.span.end.saturating_sub(1);
451                let elements = i
452                    .methods
453                    .iter()
454                    .map(|m| Self::element(&m.docs, m.sig.span, move |f| f.sig(&m.sig)))
455                    .collect();
456                let inner = self.nested(elements, close);
457                braces(head, inner)
458            }
459            ItemKind::Impl(b) => {
460                let mut head = vec![text(format!(
461                    "{}{}",
462                    b.type_name.name,
463                    self.generics(&b.generics)
464                ))];
465                if let Some(i) = &b.interface {
466                    head.push(text(": "));
467                    head.push(self.ty(i));
468                }
469                head.push(text(" {"));
470                let close = item.span.end.saturating_sub(1);
471                let elements = b
472                    .methods
473                    .iter()
474                    .map(|m| Self::element(&m.docs, m.span, move |f| f.fn_decl(&m.decl)))
475                    .collect();
476                let inner = self.nested(elements, close);
477                braces(cat(head), inner)
478            }
479            ItemKind::Fn(decl) => self.fn_decl(decl),
480            ItemKind::Test(t) => cat(vec![
481                text(format!("test {} ", self.src(t.name.span))),
482                self.block(&t.body, true),
483            ]),
484        }
485    }
486
487    /// Elements of a nested declaration, built one level deeper.
488    fn nested(&mut self, elements: Vec<Element<'_, 'a>>, close: u32) -> Doc {
489        self.depth += 1;
490        let d = self.lines(elements, close);
491        self.depth -= 1;
492        d
493    }
494
495    fn fields<'b>(fields: &'b [ast::Field]) -> Vec<Element<'b, 'a>> {
496        fields
497            .iter()
498            .map(|fl| {
499                Self::element(&fl.docs, fl.span, move |f| {
500                    cat(vec![
501                        text(format!("{}{}: ", vis(fl.public), fl.name.name)),
502                        f.ty(&fl.ty),
503                    ])
504                })
505            })
506            .collect()
507    }
508
509    fn variant(&mut self, v: &ast::Variant) -> Doc {
510        match &v.payload {
511            VariantPayload::None => text(v.name.name.clone()),
512            VariantPayload::Tuple(tys) => {
513                let items: Vec<Doc> = tys.iter().map(|t| self.ty(t)).collect();
514                cat(vec![text(v.name.name.clone()), delimited("(", ")", items)])
515            }
516            VariantPayload::Struct(fields) => {
517                if fields.iter().all(|f| f.docs.is_empty())
518                    && !self.comment_inside(v.span.start, v.span.end)
519                {
520                    let items: Vec<Doc> = fields
521                        .iter()
522                        .map(|f| cat(vec![text(format!("{}: ", f.name.name)), self.ty(&f.ty)]))
523                        .collect();
524                    cat(vec![
525                        text(format!("{} ", v.name.name)),
526                        spaced_braces(items),
527                    ])
528                } else {
529                    let close = v.span.end.saturating_sub(1);
530                    let inner = self.nested(Self::fields(fields), close);
531                    braces(text(format!("{} {{", v.name.name)), inner)
532                }
533            }
534        }
535    }
536
537    fn fn_decl(&mut self, decl: &ast::FnDecl) -> Doc {
538        let intrinsic = if decl.intrinsic { "intrinsic " } else { "" };
539        let mut parts = vec![
540            text(format!("{}{intrinsic}", vis(decl.public))),
541            self.sig(&decl.sig),
542        ];
543        if let Some(b) = &decl.body {
544            parts.push(text(" "));
545            parts.push(self.block(b, true));
546        }
547        cat(parts)
548    }
549
550    /// `main`, `main(args)`, `main -> T` or `main(args: List<String>) -> T`, as written: a
551    /// parameter whose type has an empty span was written without a type.
552    fn main_header(&mut self, sig: &ast::FnSig) -> Doc {
553        let mut parts = vec![text("main")];
554        let params: Vec<Doc> = sig
555            .params
556            .iter()
557            .map(|p| {
558                let m = mode(p.mode);
559                match &p.kind {
560                    ParamKind::SelfParam => text(format!("{m}self")),
561                    ParamKind::Named { name, ty } if ty.span.start == ty.span.end => {
562                        text(format!("{m}{}", name.name))
563                    }
564                    ParamKind::Named { name, ty } => {
565                        cat(vec![text(format!("{m}{}: ", name.name)), self.ty(ty)])
566                    }
567                }
568            })
569            .collect();
570        if !params.is_empty() {
571            parts.push(delimited("(", ")", params));
572        }
573        if let Some(r) = &sig.ret {
574            parts.push(text(" -> "));
575            parts.push(self.ty(r));
576        }
577        cat(parts)
578    }
579
580    fn sig(&mut self, sig: &ast::FnSig) -> Doc {
581        let params: Vec<Doc> = sig
582            .params
583            .iter()
584            .map(|p| {
585                let m = mode(p.mode);
586                match &p.kind {
587                    ParamKind::SelfParam => text(format!("{m}self")),
588                    ParamKind::Named { name, ty } => {
589                        cat(vec![text(format!("{m}{}: ", name.name)), self.ty(ty)])
590                    }
591                }
592            })
593            .collect();
594        let mut parts = vec![
595            text(format!(
596                "fn {}{}",
597                sig.name.name,
598                self.generics(&sig.generics)
599            )),
600            delimited("(", ")", params),
601        ];
602        if let Some(r) = &sig.ret {
603            parts.push(text(" -> "));
604            parts.push(self.ty(r));
605        }
606        cat(parts)
607    }
608
609    fn generics(&self, generics: &[GenericParam]) -> String {
610        if generics.is_empty() {
611            return String::new();
612        }
613        let list: Vec<String> = generics
614            .iter()
615            .map(|g| {
616                if g.bounds.is_empty() {
617                    g.name.name.clone()
618                } else {
619                    let b: Vec<String> = g.bounds.iter().map(|t| self.ty_text(t)).collect();
620                    format!("{}: {}", g.name.name, b.join(" + "))
621                }
622            })
623            .collect();
624        format!("<{}>", list.join(", "))
625    }
626
627    // ----- types ------------------------------------------------------------------------------
628
629    fn ty(&self, t: &Ty) -> Doc {
630        text(self.ty_text(t))
631    }
632
633    fn ty_text(&self, t: &Ty) -> String {
634        match &t.kind {
635            TyKind::Path { path: p, args } => {
636                if args.is_empty() {
637                    path(p)
638                } else {
639                    let a: Vec<String> = args.iter().map(|x| self.ty_text(x)).collect();
640                    format!("{}<{}>", path(p), a.join(", "))
641                }
642            }
643            TyKind::Fn { params, ret } => {
644                let ps: Vec<String> = params
645                    .iter()
646                    .map(|(m, x)| format!("{}{}", mode(*m), self.ty_text(x)))
647                    .collect();
648                match ret {
649                    Some(r) => format!("fn({}) -> {}", ps.join(", "), self.ty_text(r)),
650                    None => format!("fn({})", ps.join(", ")),
651                }
652            }
653            TyKind::SelfType => "Self".to_string(),
654            TyKind::Error => self.src(t.span).to_string(),
655        }
656    }
657
658    // ----- statements -------------------------------------------------------------------------
659
660    /// A block; `one_line` allows `{ x }` when the source has it on one line.
661    fn block(&mut self, b: &Block, one_line: bool) -> Doc {
662        if one_line {
663            if let Some(d) = self.one_line(b) {
664                return group(d);
665            }
666        }
667        self.multi_line(b)
668    }
669
670    /// `{ x }` without its group, if the block is written on one line with one statement.
671    fn one_line(&mut self, b: &Block) -> Option<Doc> {
672        if b.stmts.len() != 1
673            || self.src(b.span).contains('\n')
674            || self.comment_inside(b.span.start, b.span.end)
675        {
676            return None;
677        }
678        let s = b.stmts.first()?;
679        let saved = self.next;
680        self.depth += 1;
681        let d = self.stmt(s);
682        self.depth -= 1;
683        if has_hard(&d) {
684            self.next = saved;
685            return None;
686        }
687        Some(cat(vec![
688            text("{"),
689            nest(cat(vec![Doc::Line, d])),
690            Doc::Line,
691            text("}"),
692        ]))
693    }
694
695    fn multi_line(&mut self, b: &Block) -> Doc {
696        if b.stmts.is_empty() && !self.comment_inside(b.span.start, b.span.end) {
697            return text("{}");
698        }
699        self.depth += 1;
700        let elements: Vec<Element> = b
701            .stmts
702            .iter()
703            .map(|s| Element {
704                start: s.span.start,
705                end: s.span.end,
706                build: Box::new(move |f: &mut Fmt<'a>| f.stmt(s)),
707            })
708            .collect();
709        let inner = self.lines(elements, b.span.end.saturating_sub(1));
710        self.depth -= 1;
711        cat(vec![text("{"), inner, Doc::Hard, text("}")])
712    }
713
714    fn stmt(&mut self, s: &Stmt) -> Doc {
715        match &s.kind {
716            StmtKind::Let {
717                mutable,
718                name,
719                ty,
720                value,
721            } => {
722                let kw = if *mutable { "var" } else { "let" };
723                let n = name.as_ref().map_or("_".to_string(), |n| n.name.clone());
724                let mut head = vec![text(format!("{kw} {n}"))];
725                if let Some(t) = ty {
726                    head.push(text(": "));
727                    head.push(self.ty(t));
728                }
729                head.push(text(" = "));
730                self.assigned(cat(head), value)
731            }
732            StmtKind::LetPattern {
733                mutable,
734                pattern,
735                value,
736            } => {
737                let kw = if *mutable { "var" } else { "let" };
738                let head = text(format!("{kw} {} = ", self.pat(pattern)));
739                self.assigned(head, value)
740            }
741            StmtKind::Assign { target, op, value } => {
742                let head = cat(vec![
743                    self.expr(target),
744                    text(format!(" {} ", assign_op(*op))),
745                ]);
746                self.assigned(head, value)
747            }
748            StmtKind::While { label, cond, body } => cat(vec![
749                text(format!("{}while ", label_text(label))),
750                self.expr(cond),
751                text(" "),
752                self.block(body, true),
753            ]),
754            StmtKind::WhileLet {
755                label,
756                pattern,
757                mode: m,
758                value,
759                body,
760            } => cat(vec![
761                text(format!(
762                    "{}while let {} = {}",
763                    label_text(label),
764                    self.pat(pattern),
765                    mode(*m)
766                )),
767                self.expr(value),
768                text(" "),
769                self.block(body, true),
770            ]),
771            StmtKind::For {
772                label,
773                var,
774                iter,
775                body,
776            } => {
777                let mut parts = vec![text(format!("{}for {} in ", label_text(label), var.name))];
778                match iter {
779                    ForIter::Collection { mode: m, expr } => {
780                        parts.push(text(mode(*m)));
781                        parts.push(self.expr(expr));
782                    }
783                    ForIter::Range {
784                        start,
785                        end,
786                        inclusive,
787                    } => {
788                        parts.push(self.expr(start));
789                        parts.push(text(if *inclusive { "..=" } else { ".." }));
790                        parts.push(self.expr(end));
791                    }
792                }
793                parts.push(text(" "));
794                parts.push(self.block(body, true));
795                cat(parts)
796            }
797            StmtKind::Expr(e) => self.expr(e),
798        }
799    }
800
801    /// `head value`, where `head` ends with `= ` or `: Type = `: the value moves to the next line
802    /// when that makes it fit (rule 6).
803    fn assigned(&mut self, head: Doc, value: &ast::Expr) -> Doc {
804        let col = self.depth * INDENT;
805        let v = self.expr(value);
806        let head_w = flat_width(&head).unwrap_or(0);
807        if let Some(w) = flat_width(&v) {
808            let next_col = (self.depth + 1) * INDENT;
809            // Literals in brackets and braces break inside instead.
810            let literal = matches!(
811                value.kind,
812                ExprKind::List(_) | ExprKind::Map(_) | ExprKind::StructLit { .. }
813            );
814            if col + head_w + w > WIDTH && next_col + w <= WIDTH && !block_like(value) && !literal {
815                let trimmed = match head {
816                    Doc::Cat(mut parts) => {
817                        if let Some(Doc::Text(t)) = parts.last_mut() {
818                            *t = t.trim_end().to_string();
819                        }
820                        Doc::Cat(parts)
821                    }
822                    other => other,
823                };
824                return cat(vec![trimmed, nest(cat(vec![Doc::Hard, v]))]);
825            }
826        }
827        cat(vec![head, v])
828    }
829
830    // ----- expressions ------------------------------------------------------------------------
831
832    fn expr(&mut self, e: &ast::Expr) -> Doc {
833        match &e.kind {
834            ExprKind::Int(_) | ExprKind::Float(_) | ExprKind::Error => text(self.src(e.span)),
835            ExprKind::Str(_) | ExprKind::FStr(_) => self.string(e.span),
836            ExprKind::Bool(b) => text(b.to_string()),
837            ExprKind::Unit => text("()"),
838            ExprKind::Name(n) => text(n.name.clone()),
839            ExprKind::SelfValue => text("self"),
840            ExprKind::SelfType => text("Self"),
841            ExprKind::Paren(x) => cat(vec![text("("), self.expr(x), text(")")]),
842            ExprKind::List(items) => {
843                if items.is_empty() {
844                    return text("[]");
845                }
846                let docs: Vec<Doc> = items.iter().map(|x| self.expr(x)).collect();
847                delimited("[", "]", docs)
848            }
849            ExprKind::Map(entries) => {
850                if entries.is_empty() {
851                    return text("[:]");
852                }
853                let docs: Vec<Doc> = entries
854                    .iter()
855                    .map(|(k, v)| cat(vec![self.expr(k), text(": "), self.expr(v)]))
856                    .collect();
857                delimited("[", "]", docs)
858            }
859            ExprKind::StructLit {
860                path: p,
861                type_args,
862                fields,
863                base,
864            } => {
865                let mut head = path(p);
866                if !type_args.is_empty() {
867                    let a: Vec<String> = type_args.iter().map(|t| self.ty_text(t)).collect();
868                    head.push_str(&format!(".<{}>", a.join(", ")));
869                }
870                if fields.is_empty() && base.is_none() {
871                    return text(format!("{head} {{}}"));
872                }
873                let mut docs: Vec<Doc> = fields
874                    .iter()
875                    .map(|fi| match &fi.value {
876                        Some(v) => cat(vec![text(format!("{}: ", fi.name.name)), self.expr(v)]),
877                        None => text(fi.name.name.clone()),
878                    })
879                    .collect();
880                if let Some(b) = base {
881                    docs.push(cat(vec![text(".."), self.expr(b)]));
882                }
883                cat(vec![text(format!("{head} ")), spaced_braces(docs)])
884            }
885            ExprKind::Field { .. }
886            | ExprKind::Call { .. }
887            | ExprKind::TypeArgs { .. }
888            | ExprKind::Index { .. }
889            | ExprKind::Slice { .. }
890            | ExprKind::Try(_) => self.postfix(e),
891            ExprKind::Unary { op, operand } => {
892                let o = match op {
893                    UnaryOp::Neg => "-",
894                    UnaryOp::Not => "!",
895                    UnaryOp::BitNot => "~",
896                };
897                cat(vec![text(o), self.expr(operand)])
898            }
899            ExprKind::Binary { op, .. } => {
900                let mut operands = Vec::new();
901                let mut ops = Vec::new();
902                flatten(e, prec(*op), &mut operands, &mut ops);
903                // Each operand with the operator after it; the line breaks after operators.
904                let n = operands.len();
905                let mut items = Vec::new();
906                for (i, x) in operands.iter().enumerate() {
907                    let d = self.expr(x);
908                    if i + 1 < n {
909                        let o = ops.get(i).map_or("", |o: &BinaryOp| o.symbol());
910                        items.push(cat(vec![d, text(format!(" {o}"))]));
911                    } else {
912                        items.push(d);
913                    }
914                }
915                group(nest(Doc::Fill(items)))
916            }
917            ExprKind::Closure { params, ret, body } => {
918                let ps: Vec<String> = params
919                    .iter()
920                    .map(|p| match &p.ty {
921                        Some(t) => format!("{}: {}", p.name.name, self.ty_text(t)),
922                        None => p.name.name.clone(),
923                    })
924                    .collect();
925                let mut head = format!("|{}|", ps.join(", "));
926                if let Some(r) = ret {
927                    head.push_str(&format!(" -> {}", self.ty_text(r)));
928                }
929                cat(vec![text(format!("{head} ")), self.expr(body)])
930            }
931            ExprKind::Block(b) => self.block(b, true),
932            ExprKind::If { .. } | ExprKind::IfLet { .. } => {
933                // An `if` whose blocks are all on one line is one group: on one line, or every
934                // block broken.
935                if let Some(d) = self.if_one_line(e) {
936                    return group(d);
937                }
938                self.if_lines(e)
939            }
940            ExprKind::Match {
941                mode: m,
942                scrutinee,
943                arms,
944            } => {
945                let head = cat(vec![
946                    text(format!("match {}", mode(*m))),
947                    self.expr(scrutinee),
948                    text(" {"),
949                ]);
950                self.depth += 1;
951                let elements: Vec<Element> = arms
952                    .iter()
953                    .map(|a| Element {
954                        start: a.span.start,
955                        end: a.span.end,
956                        build: Box::new(move |f: &mut Fmt<'a>| f.arm(a)),
957                    })
958                    .collect();
959                let inner = self.lines(elements, e.span.end.saturating_sub(1));
960                self.depth -= 1;
961                cat(vec![head, inner, Doc::Hard, text("}")])
962            }
963            ExprKind::Return(v) => match v {
964                Some(x) => cat(vec![text("return "), self.expr(x)]),
965                None => text("return"),
966            },
967            ExprKind::Loop { label, body } => cat(vec![
968                text(format!("{}loop ", label_text(label))),
969                self.block(body, true),
970            ]),
971            ExprKind::Break { label, value } => match (label, value) {
972                (Some(l), _) => text(format!("break {}", l.name)),
973                (None, Some(v)) => cat(vec![text("break "), self.expr(v)]),
974                (None, None) => text("break"),
975            },
976            ExprKind::Continue { label } => match label {
977                Some(l) => text(format!("continue {}", l.name)),
978                None => text("continue"),
979            },
980        }
981    }
982
983    /// A string literal as written. A multiline string keeps its content and moves with the
984    /// indentation of the code (chapter 3, section 8a, rule 8).
985    fn string(&self, span: Span) -> Doc {
986        let source = self.src(span);
987        let lines: Vec<&str> = source
988            .split('\n')
989            .map(|l| l.trim_end_matches('\r'))
990            .collect();
991        let (Some(first), Some(last)) = (lines.first(), lines.last()) else {
992            return text(source);
993        };
994        if lines.len() < 2 {
995            return text(source);
996        }
997        let indent = &last[..last.len() - last.trim_start().len()];
998        let content = lines
999            .get(1..lines.len() - 1)
1000            .unwrap_or_default()
1001            .iter()
1002            .map(|l| match l.strip_prefix(indent) {
1003                Some(rest) => rest.to_string(),
1004                None => l.trim_start().to_string(),
1005            })
1006            .collect();
1007        Doc::Verbatim(
1008            first.trim_end().to_string(),
1009            content,
1010            last.trim_start().to_string(),
1011        )
1012    }
1013
1014    /// The head of an `if` or an `if let` up to its first block, the block and the `else` part.
1015    fn if_head<'e>(
1016        &mut self,
1017        e: &'e ast::Expr,
1018    ) -> Option<(Vec<Doc>, &'e ast::Block, Option<&'e ast::Expr>)> {
1019        match &e.kind {
1020            ExprKind::If {
1021                cond,
1022                then,
1023                otherwise,
1024            } => Some((
1025                vec![text("if "), self.expr(cond), text(" ")],
1026                then,
1027                otherwise.as_deref(),
1028            )),
1029            ExprKind::IfLet {
1030                pattern,
1031                mode: m,
1032                value,
1033                then,
1034                otherwise,
1035            } => Some((
1036                vec![
1037                    text(format!("if let {} = {}", self.pat(pattern), mode(*m))),
1038                    self.expr(value),
1039                    text(" "),
1040                ],
1041                then,
1042                otherwise.as_deref(),
1043            )),
1044            _ => None,
1045        }
1046    }
1047
1048    fn if_one_line(&mut self, e: &ast::Expr) -> Option<Doc> {
1049        let saved = self.next;
1050        let (mut parts, then, otherwise) = self.if_head(e)?;
1051        let Some(t) = self.one_line(then) else {
1052            self.next = saved;
1053            return None;
1054        };
1055        parts.push(t);
1056        if let Some(o) = otherwise {
1057            parts.push(text(" else "));
1058            let rest = match &o.kind {
1059                ExprKind::Block(b) => self.one_line(b),
1060                ExprKind::If { .. } | ExprKind::IfLet { .. } => self.if_one_line(o),
1061                _ => None,
1062            };
1063            let Some(r) = rest else {
1064                self.next = saved;
1065                return None;
1066            };
1067            parts.push(r);
1068        }
1069        Some(cat(parts))
1070    }
1071
1072    fn if_lines(&mut self, e: &ast::Expr) -> Doc {
1073        let Some((mut parts, then, otherwise)) = self.if_head(e) else {
1074            return self.expr(e);
1075        };
1076        parts.push(self.multi_line(then));
1077        if let Some(o) = otherwise {
1078            parts.push(text(" else "));
1079            match &o.kind {
1080                ExprKind::Block(b) => parts.push(self.multi_line(b)),
1081                ExprKind::If { .. } | ExprKind::IfLet { .. } => parts.push(self.if_lines(o)),
1082                _ => parts.push(self.expr(o)),
1083            }
1084        }
1085        cat(parts)
1086    }
1087
1088    fn arm(&mut self, a: &Arm) -> Doc {
1089        let mut parts = vec![text(self.pat(&a.pattern))];
1090        if let Some(g) = &a.guard {
1091            parts.push(text(" if "));
1092            parts.push(self.expr(g));
1093        }
1094        parts.push(text(" => "));
1095        parts.push(self.expr(&a.body));
1096        cat(parts)
1097    }
1098
1099    fn pat(&self, p: &Pat) -> String {
1100        match &p.kind {
1101            PatKind::Wild => "_".to_string(),
1102            PatKind::Name(n) => n.name.clone(),
1103            PatKind::Int { .. } | PatKind::Str(_) | PatKind::Error => self.src(p.span).to_string(),
1104            PatKind::Bool(b) => b.to_string(),
1105            PatKind::Path(x) => path(x),
1106            PatKind::Tuple { path: x, items } => {
1107                let i: Vec<String> = items.iter().map(|y| self.pat(y)).collect();
1108                format!("{}({})", path(x), i.join(", "))
1109            }
1110            PatKind::Struct {
1111                path: x,
1112                fields,
1113                rest,
1114            } => {
1115                let mut i: Vec<String> = fields
1116                    .iter()
1117                    .map(|f| match &f.pat {
1118                        Some(y) => format!("{}: {}", f.name.name, self.pat(y)),
1119                        None => f.name.name.clone(),
1120                    })
1121                    .collect();
1122                if *rest {
1123                    i.push("..".to_string());
1124                }
1125                if i.is_empty() {
1126                    format!("{} {{}}", path(x))
1127                } else {
1128                    format!("{} {{ {} }}", path(x), i.join(", "))
1129                }
1130            }
1131            PatKind::Or(alts) => {
1132                let a: Vec<String> = alts.iter().map(|y| self.pat(y)).collect();
1133                a.join(" | ")
1134            }
1135        }
1136    }
1137
1138    /// Field access, calls, indexing and `?`: a chain of two or more method calls puts each
1139    /// call on its own line when it does not fit (rule 6).
1140    fn postfix(&mut self, e: &ast::Expr) -> Doc {
1141        let mut ops: Vec<&ast::Expr> = Vec::new();
1142        let mut cur = e;
1143        while let ExprKind::Field { base, .. }
1144        | ExprKind::Call { callee: base, .. }
1145        | ExprKind::TypeArgs { base, .. }
1146        | ExprKind::Index { base, .. }
1147        | ExprKind::Slice { base, .. }
1148        | ExprKind::Try(base) = &cur.kind
1149        {
1150            ops.push(cur);
1151            cur = base;
1152        }
1153        ops.reverse();
1154        let calls = ops
1155            .iter()
1156            .filter(|x| {
1157                matches!(&x.kind, ExprKind::Call { callee, .. } if matches!(callee.kind, ExprKind::Field { .. }))
1158            })
1159            .count();
1160        let base = self.expr(cur);
1161        if calls < 2 {
1162            let mut parts = vec![base];
1163            for op in ops {
1164                parts.push(self.postfix_op(op));
1165            }
1166            return cat(parts);
1167        }
1168        let mut chain = Vec::new();
1169        for op in ops {
1170            if matches!(op.kind, ExprKind::Field { .. }) {
1171                chain.push(Doc::Soft);
1172            }
1173            chain.push(self.postfix_op(op));
1174        }
1175        group(cat(vec![base, nest(cat(chain))]))
1176    }
1177
1178    fn postfix_op(&mut self, op: &ast::Expr) -> Doc {
1179        match &op.kind {
1180            ExprKind::Field { name, .. } => text(format!(".{}", name.name)),
1181            ExprKind::TypeArgs { args, .. } => {
1182                let a: Vec<String> = args.iter().map(|t| self.ty_text(t)).collect();
1183                text(format!(".<{}>", a.join(", ")))
1184            }
1185            ExprKind::Call { args, .. } => self.args(args),
1186            ExprKind::Index { index, .. } => cat(vec![text("["), self.expr(index), text("]")]),
1187            ExprKind::Slice {
1188                start,
1189                end,
1190                inclusive,
1191                ..
1192            } => {
1193                let mut parts = vec![text("[")];
1194                if let Some(s) = start {
1195                    parts.push(self.expr(s));
1196                }
1197                parts.push(text(if *inclusive { "..=" } else { ".." }));
1198                if let Some(x) = end {
1199                    parts.push(self.expr(x));
1200                }
1201                parts.push(text("]"));
1202                cat(parts)
1203            }
1204            ExprKind::Try(_) => text("?"),
1205            _ => text(""),
1206        }
1207    }
1208
1209    fn args(&mut self, args: &[Arg]) -> Doc {
1210        if args.is_empty() {
1211            return text("()");
1212        }
1213        let docs: Vec<Doc> = args
1214            .iter()
1215            .map(|a| cat(vec![text(mode(a.mode)), self.expr(&a.value)]))
1216            .collect();
1217        // A last argument that is a closure with a block, a `match`, an `if` or a block stays on
1218        // the line of the call.
1219        let hug = args.last().is_some_and(|a| huggable(&a.value))
1220            && docs.last().is_some_and(has_hard)
1221            && docs.iter().rev().skip(1).all(|d| !has_hard(d));
1222        if hug {
1223            let mut parts = vec![text("(")];
1224            let n = docs.len();
1225            for (i, d) in docs.into_iter().enumerate() {
1226                if i + 1 < n {
1227                    parts.push(flat(d));
1228                    parts.push(text(", "));
1229                } else {
1230                    parts.push(d);
1231                }
1232            }
1233            parts.push(text(")"));
1234            return cat(parts);
1235        }
1236        delimited("(", ")", docs)
1237    }
1238}
1239
1240/// `open items close` on one line, or one item per line with a trailing comma.
1241fn delimited(open: &str, close: &str, items: Vec<Doc>) -> Doc {
1242    let mut inner = vec![Doc::Soft];
1243    let n = items.len();
1244    for (i, d) in items.into_iter().enumerate() {
1245        inner.push(d);
1246        if i + 1 < n {
1247            inner.push(text(","));
1248            inner.push(Doc::Line);
1249        }
1250    }
1251    inner.push(Doc::IfBreak(",".to_string()));
1252    group(cat(vec![
1253        text(open),
1254        nest(cat(inner)),
1255        Doc::Soft,
1256        text(close),
1257    ]))
1258}
1259
1260/// `{ a, b }` on one line, or one item per line with a trailing comma.
1261fn spaced_braces(items: Vec<Doc>) -> Doc {
1262    let mut inner = vec![Doc::Line];
1263    let n = items.len();
1264    for (i, d) in items.into_iter().enumerate() {
1265        inner.push(d);
1266        if i + 1 < n {
1267            inner.push(text(","));
1268            inner.push(Doc::Line);
1269        }
1270    }
1271    inner.push(Doc::IfBreak(",".to_string()));
1272    group(cat(vec![text("{"), nest(cat(inner)), Doc::Line, text("}")]))
1273}
1274
1275/// `head` (ending with `{`), the nested lines, and the closing brace.
1276fn braces(head: Doc, inner: Doc) -> Doc {
1277    cat(vec![head, inner, Doc::Hard, text("}")])
1278}
1279
1280fn huggable(e: &ast::Expr) -> bool {
1281    match &e.kind {
1282        ExprKind::Closure { body, .. } => block_like(body),
1283        _ => block_like(e),
1284    }
1285}
1286
1287fn block_like(e: &ast::Expr) -> bool {
1288    matches!(
1289        e.kind,
1290        ExprKind::Block(_)
1291            | ExprKind::Match { .. }
1292            | ExprKind::If { .. }
1293            | ExprKind::IfLet { .. }
1294            | ExprKind::Loop { .. }
1295    )
1296}
1297
1298/// `label: ` before a loop, or nothing.
1299fn label_text(label: &Option<ast::Ident>) -> String {
1300    label
1301        .as_ref()
1302        .map_or(String::new(), |l| format!("{}: ", l.name))
1303}
1304
1305/// The operands and operators of a chain of binary operators of one precedence.
1306fn flatten<'e>(
1307    e: &'e ast::Expr,
1308    level: u8,
1309    operands: &mut Vec<&'e ast::Expr>,
1310    ops: &mut Vec<BinaryOp>,
1311) {
1312    match &e.kind {
1313        ExprKind::Binary { op, lhs, rhs } if prec(*op) == level => {
1314            flatten(lhs, level, operands, ops);
1315            ops.push(*op);
1316            operands.push(rhs);
1317        }
1318        _ => operands.push(e),
1319    }
1320}
1321
1322fn prec(op: BinaryOp) -> u8 {
1323    use BinaryOp as B;
1324    match op {
1325        B::Mul | B::Div | B::Rem => 10,
1326        B::Add | B::Sub => 9,
1327        B::Shl | B::Shr => 8,
1328        B::BitAnd => 7,
1329        B::BitXor => 6,
1330        B::BitOr => 5,
1331        B::Eq | B::NotEq | B::Lt | B::Le | B::Gt | B::Ge => 4,
1332        B::And => 3,
1333        B::Or => 2,
1334    }
1335}
1336
1337fn path(p: &ast::Path) -> String {
1338    p.segments
1339        .iter()
1340        .map(|s| s.name.as_str())
1341        .collect::<Vec<_>>()
1342        .join(".")
1343}
1344
1345fn vis(public: bool) -> &'static str {
1346    if public { "pub " } else { "" }
1347}
1348
1349fn mode(m: Mode) -> &'static str {
1350    match m {
1351        Mode::Read => "",
1352        Mode::Mut => "mut ",
1353        Mode::Own => "own ",
1354    }
1355}
1356
1357fn assign_op(op: AssignOp) -> &'static str {
1358    match op {
1359        AssignOp::Assign => "=",
1360        AssignOp::Add => "+=",
1361        AssignOp::Sub => "-=",
1362        AssignOp::Mul => "*=",
1363        AssignOp::Div => "/=",
1364        AssignOp::Rem => "%=",
1365        AssignOp::BitAnd => "&=",
1366        AssignOp::BitOr => "|=",
1367        AssignOp::BitXor => "^=",
1368        AssignOp::Shl => "<<=",
1369        AssignOp::Shr => ">>=",
1370    }
1371}