1mod 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#[derive(Debug)]
22pub enum FmtError {
23 Syntax(Vec<Diagnostic>),
25 Internal(String),
27}
28
29pub 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
59fn 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 comments: Vec<(u32, u32)>,
103 next: usize,
105 depth: usize,
107}
108
109struct 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 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 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 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 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 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 while matches!(rest.last(), Some(Doc::Hard)) {
226 rest.pop();
227 }
228 parts.extend(rest);
229 }
230 nest(cat(parts))
231 }
232
233 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
1240fn 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
1260fn 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
1275fn 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
1298fn label_text(label: &Option<ast::Ident>) -> String {
1300 label
1301 .as_ref()
1302 .map_or(String::new(), |l| format!("{}: ", l.name))
1303}
1304
1305fn 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}