Skip to main content

bock_fmt/
emit.rs

1//! AST-to-source emitter with canonical formatting.
2
3use bock_ast::{
4    Annotation, Arg, AssignOp, BinOp, Block, ClassDecl, ConstDecl, EffectDecl, EnumDecl,
5    EnumVariant, Expr, FnDecl, ForLoop, GenericParam, GuardStmt, HandlingBlock, ImplBlock,
6    ImportDecl, ImportItems, ImportedName, InterpolationPart, Item, LetStmt, Literal, LoopStmt,
7    MatchArm, Module, ModuleHandleDecl, ModulePath, Param, Pattern, PropertyTestDecl, RecordDecl,
8    RecordDeclField, Stmt, TraitDecl, TypeAliasDecl, TypeConstraint, TypeExpr, TypePath, UnaryOp,
9    Visibility, WhileLoop,
10};
11
12use crate::comments::{Comment, CommentKind};
13
14/// Soft line-length limit (prefer to stay within).
15const SOFT_LIMIT: usize = 80;
16
17/// Hard line-length limit (must not exceed).
18const HARD_LIMIT: usize = 100;
19
20/// Number of spaces per indentation level.
21const INDENT_WIDTH: usize = 2;
22
23/// The canonical formatter. Walks the AST and emits formatted source text.
24pub struct Formatter<'a> {
25    /// Output buffer.
26    buf: String,
27    /// Current indentation level (number of levels, not spaces).
28    indent: usize,
29    /// All comments extracted from the original source.
30    comments: &'a [Comment],
31    /// Index of the next comment to potentially emit.
32    next_comment: usize,
33    /// Original source text (for comment position mapping).
34    source: &'a str,
35}
36
37impl<'a> Formatter<'a> {
38    /// Create a new formatter.
39    #[must_use]
40    pub fn new(comments: &'a [Comment], source: &'a str) -> Self {
41        Self {
42            buf: String::with_capacity(source.len()),
43            indent: 0,
44            comments,
45            next_comment: 0,
46            source,
47        }
48    }
49
50    /// Consume the formatter and return the formatted output.
51    #[must_use]
52    pub fn finish(mut self) -> String {
53        // Emit any trailing comments
54        self.emit_remaining_comments();
55        // Ensure file ends with a single newline
56        let trimmed = self.buf.trim_end().to_string();
57        if trimmed.is_empty() {
58            return String::new();
59        }
60        self.buf = trimmed;
61        self.buf.push('\n');
62        // Enforce hard line-length limit
63        wrap_long_lines(&self.buf)
64    }
65
66    // ─── Indentation helpers ──────────────────────────────────────────────
67
68    fn indent_str(&self) -> String {
69        " ".repeat(self.indent * INDENT_WIDTH)
70    }
71
72    fn push_indent(&mut self) {
73        let indent = self.indent_str();
74        self.buf.push_str(&indent);
75    }
76
77    fn inc_indent(&mut self) {
78        self.indent += 1;
79    }
80
81    fn dec_indent(&mut self) {
82        self.indent = self.indent.saturating_sub(1);
83    }
84
85    // ─── Output helpers ──────────────────────────────────────────────────
86
87    fn push(&mut self, s: &str) {
88        self.buf.push_str(s);
89    }
90
91    fn push_char(&mut self, c: char) {
92        self.buf.push(c);
93    }
94
95    fn newline(&mut self) {
96        self.buf.push('\n');
97    }
98
99    fn push_line(&mut self, s: &str) {
100        self.push_indent();
101        self.push(s);
102        self.newline();
103    }
104
105    /// Estimate the length of a formatted expression (for line-length decisions).
106    fn estimate_expr_len(&self, expr: &Expr) -> usize {
107        let mut f = Formatter::new(&[], "");
108        f.format_expr(expr);
109        f.buf.len()
110    }
111
112    /// Estimate the length of a formatted type expression.
113    fn estimate_type_len(&self, ty: &TypeExpr) -> usize {
114        let mut f = Formatter::new(&[], "");
115        f.format_type_expr(ty);
116        f.buf.len()
117    }
118
119    /// Check whether a function signature fits on one line.
120    fn sig_fits_one_line(&self, decl: &FnDecl) -> bool {
121        let mut est = 0;
122        // "fn name("
123        est += 3 + decl.name.name.len() + 1;
124        for (i, p) in decl.params.iter().enumerate() {
125            if i > 0 {
126                est += 2; // ", "
127            }
128            est += self.estimate_param_len(p);
129        }
130        est += 1; // ")"
131        if let Some(ret) = &decl.return_type {
132            est += 4; // " -> "
133            est += self.estimate_type_len(ret);
134        }
135        est += 2; // " {"
136        self.indent * INDENT_WIDTH + est <= SOFT_LIMIT
137    }
138
139    fn estimate_param_len(&self, p: &Param) -> usize {
140        let mut est = self.estimate_pattern_len(&p.pattern);
141        if let Some(ty) = &p.ty {
142            est += 2; // ": "
143            est += self.estimate_type_len(ty);
144        }
145        if let Some(def) = &p.default {
146            est += 3; // " = "
147            est += self.estimate_expr_len(def);
148        }
149        est
150    }
151
152    fn estimate_pattern_len(&self, pat: &Pattern) -> usize {
153        let mut f = Formatter::new(&[], "");
154        f.format_pattern(pat);
155        f.buf.len()
156    }
157
158    // ─── Comment emission ──────────────────────────────────────────────────
159
160    /// Emit any line or block comments that appear before `byte_offset` in
161    /// the original source.
162    fn emit_comments_before(&mut self, byte_offset: usize) {
163        while self.next_comment < self.comments.len() {
164            let c = &self.comments[self.next_comment];
165            if c.start >= byte_offset {
166                break;
167            }
168            // Skip doc comments — they're handled by the AST
169            if c.kind == CommentKind::Doc || c.kind == CommentKind::ModuleDoc {
170                self.next_comment += 1;
171                continue;
172            }
173
174            // Determine if this is an inline comment (on the same line as code before it)
175            let is_inline = c.start > 0 && {
176                let before = &self.source[..c.start];
177                let last_nl = before.rfind('\n').map_or(0, |p| p + 1);
178                let line_before = before[last_nl..].trim();
179                !line_before.is_empty()
180            };
181
182            if is_inline {
183                // Inline comment: append to current line
184                // Trim any trailing whitespace from buffer, add two spaces
185                let trimmed_end = self.buf.trim_end_matches(' ').len();
186                self.buf.truncate(trimmed_end);
187                self.push("  ");
188                self.push(&c.text);
189            } else {
190                // Standalone comment: emit on its own line
191                self.push_indent();
192                self.push(&c.text);
193                self.newline();
194            }
195            self.next_comment += 1;
196        }
197    }
198
199    /// Emit any remaining comments at the end of the file.
200    fn emit_remaining_comments(&mut self) {
201        while self.next_comment < self.comments.len() {
202            let c = &self.comments[self.next_comment];
203            if c.kind == CommentKind::Doc || c.kind == CommentKind::ModuleDoc {
204                self.next_comment += 1;
205                continue;
206            }
207            self.newline();
208            self.push_indent();
209            self.push(&c.text);
210            self.next_comment += 1;
211        }
212    }
213
214    // ─── Module ───────────────────────────────────────────────────────────
215
216    /// Format a complete module.
217    pub fn format_module(&mut self, module: &Module) {
218        // Module doc comments
219        for doc in &module.doc {
220            self.push("//! ");
221            self.push(doc);
222            self.newline();
223        }
224        if !module.doc.is_empty() {
225            self.newline();
226        }
227
228        // Module path
229        if let Some(path) = &module.path {
230            self.push("module ");
231            self.format_module_path(path);
232            self.newline();
233            self.newline();
234        }
235
236        // Imports (sorted)
237        if !module.imports.is_empty() {
238            let mut sorted_imports = module.imports.clone();
239            sorted_imports.sort_by(|a, b| {
240                let cat_a = import_category(a);
241                let cat_b = import_category(b);
242                cat_a
243                    .cmp(&cat_b)
244                    .then_with(|| import_path_str(a).cmp(&import_path_str(b)))
245            });
246
247            // Group by category with blank lines between groups
248            let mut prev_cat = None;
249            for import in &sorted_imports {
250                let cat = import_category(import);
251                if let Some(prev) = prev_cat {
252                    if prev != cat {
253                        self.newline();
254                    }
255                }
256                self.emit_comments_before(import.span.start);
257                self.format_import(import);
258                prev_cat = Some(cat);
259            }
260            self.newline();
261        }
262
263        // Items
264        let mut first = true;
265        for item in &module.items {
266            if !first {
267                self.newline();
268            }
269            self.emit_comments_before(item.span().start);
270            self.format_item(item);
271            first = false;
272        }
273    }
274
275    // ─── Imports ──────────────────────────────────────────────────────────
276
277    fn format_import(&mut self, import: &ImportDecl) {
278        self.push("use ");
279        self.format_module_path(&import.path);
280        match &import.items {
281            ImportItems::Module => {}
282            ImportItems::Glob => {
283                self.push(".*");
284            }
285            ImportItems::Named(names) => {
286                self.push(".{ ");
287                for (i, name) in names.iter().enumerate() {
288                    if i > 0 {
289                        self.push(", ");
290                    }
291                    self.format_imported_name(name);
292                }
293                self.push(" }");
294            }
295        }
296        self.newline();
297    }
298
299    fn format_imported_name(&mut self, name: &ImportedName) {
300        self.push(&name.name.name);
301        if let Some(alias) = &name.alias {
302            self.push(" as ");
303            self.push(&alias.name);
304        }
305    }
306
307    fn format_module_path(&mut self, path: &ModulePath) {
308        for (i, seg) in path.segments.iter().enumerate() {
309            if i > 0 {
310                self.push_char('.');
311            }
312            self.push(&seg.name);
313        }
314    }
315
316    // ─── Items ────────────────────────────────────────────────────────────
317
318    fn format_item(&mut self, item: &Item) {
319        match item {
320            Item::Fn(decl) => self.format_fn_decl(decl),
321            Item::Record(decl) => self.format_record_decl(decl),
322            Item::Enum(decl) => self.format_enum_decl(decl),
323            Item::Class(decl) => self.format_class_decl(decl),
324            Item::Trait(decl) | Item::PlatformTrait(decl) => {
325                self.format_trait_decl(decl);
326            }
327            Item::Impl(decl) => self.format_impl_block(decl),
328            Item::Effect(decl) => self.format_effect_decl(decl),
329            Item::TypeAlias(decl) => self.format_type_alias(decl),
330            Item::Const(decl) => self.format_const_decl(decl),
331            Item::ModuleHandle(decl) => self.format_module_handle(decl),
332            Item::PropertyTest(decl) => self.format_property_test(decl),
333            Item::Error { .. } => {}
334        }
335    }
336
337    // ─── Annotations ──────────────────────────────────────────────────────
338
339    fn format_annotations(&mut self, annotations: &[Annotation]) {
340        for ann in annotations {
341            self.push_indent();
342            self.push_char('@');
343            self.push(&ann.name.name);
344            if !ann.args.is_empty() {
345                self.push_char('(');
346                for (i, arg) in ann.args.iter().enumerate() {
347                    if i > 0 {
348                        self.push(", ");
349                    }
350                    if let Some(label) = &arg.label {
351                        self.push(&label.name);
352                        self.push(": ");
353                    }
354                    self.format_expr(&arg.value);
355                }
356                self.push_char(')');
357            }
358            self.newline();
359        }
360    }
361
362    // ─── Visibility ───────────────────────────────────────────────────────
363
364    fn format_visibility(&mut self, vis: Visibility) {
365        match vis {
366            Visibility::Private => {}
367            Visibility::Internal => self.push("internal "),
368            Visibility::Public => self.push("pub "),
369        }
370    }
371
372    // ─── Functions ────────────────────────────────────────────────────────
373
374    fn format_fn_decl(&mut self, decl: &FnDecl) {
375        // Doc comments from AST (already in annotations? no — in the AST as Module.doc)
376        // Doc comments for items are tracked via span-based comment emission
377        self.format_annotations(&decl.annotations);
378        self.push_indent();
379        self.format_visibility(decl.visibility);
380        if decl.is_async {
381            self.push("async ");
382        }
383        self.push("fn ");
384        self.push(&decl.name.name);
385
386        // Generic params
387        self.format_generic_params(&decl.generic_params);
388
389        if self.sig_fits_one_line(decl) {
390            // Single-line signature
391            self.push_char('(');
392            for (i, p) in decl.params.iter().enumerate() {
393                if i > 0 {
394                    self.push(", ");
395                }
396                self.format_param(p);
397            }
398            self.push_char(')');
399        } else {
400            // Multi-line: one param per line
401            self.push_char('(');
402            self.newline();
403            self.inc_indent();
404            for (i, p) in decl.params.iter().enumerate() {
405                self.push_indent();
406                self.format_param(p);
407                if i < decl.params.len() - 1 {
408                    self.push_char(',');
409                } else {
410                    self.push_char(','); // trailing comma
411                }
412                self.newline();
413            }
414            self.dec_indent();
415            self.push_indent();
416            self.push_char(')');
417        }
418
419        // Return type
420        if let Some(ret) = &decl.return_type {
421            self.push(" -> ");
422            self.format_type_expr(ret);
423        }
424
425        // Effect clause
426        if !decl.effect_clause.is_empty() {
427            self.push(" with ");
428            for (i, eff) in decl.effect_clause.iter().enumerate() {
429                if i > 0 {
430                    self.push(", ");
431                }
432                self.format_type_path(eff);
433            }
434        }
435
436        // Where clause
437        if !decl.where_clause.is_empty() {
438            self.newline();
439            self.inc_indent();
440            self.push_indent();
441            self.push("where ");
442            for (i, c) in decl.where_clause.iter().enumerate() {
443                if i > 0 {
444                    self.push(", ");
445                }
446                self.format_type_constraint(c);
447            }
448            self.dec_indent();
449        }
450
451        if let Some(body) = &decl.body {
452            self.push(" ");
453            self.format_block(body);
454        }
455        self.newline();
456    }
457
458    fn format_param(&mut self, param: &Param) {
459        self.format_pattern(&param.pattern);
460        if let Some(ty) = &param.ty {
461            self.push(": ");
462            self.format_type_expr(ty);
463        }
464        if let Some(def) = &param.default {
465            self.push(" = ");
466            self.format_expr(def);
467        }
468    }
469
470    fn format_generic_params(&mut self, params: &[GenericParam]) {
471        if params.is_empty() {
472            return;
473        }
474        self.push_char('[');
475        for (i, p) in params.iter().enumerate() {
476            if i > 0 {
477                self.push(", ");
478            }
479            self.push(&p.name.name);
480            if !p.bounds.is_empty() {
481                self.push(": ");
482                for (j, b) in p.bounds.iter().enumerate() {
483                    if j > 0 {
484                        self.push(" + ");
485                    }
486                    self.format_type_path(b);
487                }
488            }
489        }
490        self.push_char(']');
491    }
492
493    fn format_type_constraint(&mut self, c: &TypeConstraint) {
494        self.push(&c.param.name);
495        self.push(": ");
496        for (i, b) in c.bounds.iter().enumerate() {
497            if i > 0 {
498                self.push(" + ");
499            }
500            self.format_type_path(b);
501        }
502    }
503
504    // ─── Records ──────────────────────────────────────────────────────────
505
506    fn format_record_decl(&mut self, decl: &RecordDecl) {
507        self.format_annotations(&decl.annotations);
508        self.push_indent();
509        self.format_visibility(decl.visibility);
510        self.push("record ");
511        self.push(&decl.name.name);
512        self.format_generic_params(&decl.generic_params);
513        self.push(" {");
514        self.newline();
515        self.inc_indent();
516        for field in &decl.fields {
517            self.emit_comments_before(field.span.start);
518            self.format_record_field_decl(field);
519        }
520        self.dec_indent();
521        self.push_line("}");
522    }
523
524    fn format_record_field_decl(&mut self, field: &RecordDeclField) {
525        self.push_indent();
526        self.push(&field.name.name);
527        self.push(": ");
528        self.format_type_expr(&field.ty);
529        if let Some(def) = &field.default {
530            self.push(" = ");
531            self.format_expr(def);
532        }
533        self.push_char(',');
534        self.newline();
535    }
536
537    // ─── Enums ────────────────────────────────────────────────────────────
538
539    fn format_enum_decl(&mut self, decl: &EnumDecl) {
540        self.format_annotations(&decl.annotations);
541        self.push_indent();
542        self.format_visibility(decl.visibility);
543        self.push("enum ");
544        self.push(&decl.name.name);
545        self.format_generic_params(&decl.generic_params);
546        self.push(" {");
547        self.newline();
548        self.inc_indent();
549        for variant in &decl.variants {
550            self.format_enum_variant(variant);
551        }
552        self.dec_indent();
553        self.push_line("}");
554    }
555
556    fn format_enum_variant(&mut self, variant: &EnumVariant) {
557        self.push_indent();
558        match variant {
559            EnumVariant::Unit { name, .. } => {
560                self.push(&name.name);
561                self.push_char(',');
562            }
563            EnumVariant::Tuple { name, tys, .. } => {
564                self.push(&name.name);
565                self.push_char('(');
566                for (i, ty) in tys.iter().enumerate() {
567                    if i > 0 {
568                        self.push(", ");
569                    }
570                    self.format_type_expr(ty);
571                }
572                self.push("),");
573            }
574            EnumVariant::Struct { name, fields, .. } => {
575                self.push(&name.name);
576                self.push(" {");
577                self.newline();
578                self.inc_indent();
579                for field in fields {
580                    self.format_record_field_decl(field);
581                }
582                self.dec_indent();
583                self.push_indent();
584                self.push("},");
585            }
586        }
587        self.newline();
588    }
589
590    // ─── Classes ──────────────────────────────────────────────────────────
591
592    fn format_class_decl(&mut self, decl: &ClassDecl) {
593        self.format_annotations(&decl.annotations);
594        self.push_indent();
595        self.format_visibility(decl.visibility);
596        self.push("class ");
597        self.push(&decl.name.name);
598        self.format_generic_params(&decl.generic_params);
599        if let Some(base) = &decl.base {
600            self.push(" extends ");
601            self.format_type_path(base);
602        }
603        if !decl.traits.is_empty() {
604            self.push(" impl ");
605            for (i, t) in decl.traits.iter().enumerate() {
606                if i > 0 {
607                    self.push(", ");
608                }
609                self.format_type_path(t);
610            }
611        }
612        self.push(" {");
613        self.newline();
614        self.inc_indent();
615        for field in &decl.fields {
616            self.format_record_field_decl(field);
617        }
618        if !decl.fields.is_empty() && !decl.methods.is_empty() {
619            self.newline();
620        }
621        for (i, method) in decl.methods.iter().enumerate() {
622            if i > 0 {
623                self.newline();
624            }
625            self.format_fn_decl(method);
626        }
627        self.dec_indent();
628        self.push_line("}");
629    }
630
631    // ─── Traits ───────────────────────────────────────────────────────────
632
633    fn format_trait_decl(&mut self, decl: &TraitDecl) {
634        self.format_annotations(&decl.annotations);
635        self.push_indent();
636        self.format_visibility(decl.visibility);
637        if decl.is_platform {
638            self.push("platform ");
639        }
640        self.push("trait ");
641        self.push(&decl.name.name);
642        self.format_generic_params(&decl.generic_params);
643        self.push(" {");
644        self.newline();
645        self.inc_indent();
646        for assoc in &decl.associated_types {
647            self.push_indent();
648            self.push("type ");
649            self.push(&assoc.name.name);
650            if !assoc.bounds.is_empty() {
651                self.push(": ");
652                for (i, b) in assoc.bounds.iter().enumerate() {
653                    if i > 0 {
654                        self.push(" + ");
655                    }
656                    self.format_type_path(b);
657                }
658            }
659            self.newline();
660        }
661        if !decl.associated_types.is_empty() && !decl.methods.is_empty() {
662            self.newline();
663        }
664        for (i, method) in decl.methods.iter().enumerate() {
665            if i > 0 {
666                self.newline();
667            }
668            self.format_fn_decl(method);
669        }
670        self.dec_indent();
671        self.push_line("}");
672    }
673
674    // ─── Impl blocks ─────────────────────────────────────────────────────
675
676    fn format_impl_block(&mut self, decl: &ImplBlock) {
677        self.format_annotations(&decl.annotations);
678        self.push_indent();
679        self.push("impl ");
680        self.format_generic_params(&decl.generic_params);
681        if !decl.generic_params.is_empty() {
682            self.push_char(' ');
683        }
684        if let Some(trait_path) = &decl.trait_path {
685            self.format_type_path(trait_path);
686            self.push(" for ");
687        }
688        self.format_type_expr(&decl.target);
689        if !decl.where_clause.is_empty() {
690            self.newline();
691            self.inc_indent();
692            self.push_indent();
693            self.push("where ");
694            for (i, c) in decl.where_clause.iter().enumerate() {
695                if i > 0 {
696                    self.push(", ");
697                }
698                self.format_type_constraint(c);
699            }
700            self.dec_indent();
701        }
702        self.push(" {");
703        self.newline();
704        self.inc_indent();
705        for (i, method) in decl.methods.iter().enumerate() {
706            if i > 0 {
707                self.newline();
708            }
709            self.format_fn_decl(method);
710        }
711        self.dec_indent();
712        self.push_line("}");
713    }
714
715    // ─── Effects ──────────────────────────────────────────────────────────
716
717    fn format_effect_decl(&mut self, decl: &EffectDecl) {
718        self.format_annotations(&decl.annotations);
719        self.push_indent();
720        self.format_visibility(decl.visibility);
721        self.push("effect ");
722        self.push(&decl.name.name);
723        self.format_generic_params(&decl.generic_params);
724        self.push(" {");
725        self.newline();
726        self.inc_indent();
727        for (i, op) in decl.operations.iter().enumerate() {
728            if i > 0 {
729                self.newline();
730            }
731            self.format_fn_decl(op);
732        }
733        self.dec_indent();
734        self.push_line("}");
735    }
736
737    // ─── Type aliases ─────────────────────────────────────────────────────
738
739    fn format_type_alias(&mut self, decl: &TypeAliasDecl) {
740        self.format_annotations(&decl.annotations);
741        self.push_indent();
742        self.format_visibility(decl.visibility);
743        self.push("type ");
744        self.push(&decl.name.name);
745        self.format_generic_params(&decl.generic_params);
746        self.push(" = ");
747        self.format_type_expr(&decl.ty);
748        if !decl.where_clause.is_empty() {
749            self.push(" where ");
750            for (i, c) in decl.where_clause.iter().enumerate() {
751                if i > 0 {
752                    self.push(", ");
753                }
754                self.format_type_constraint(c);
755            }
756        }
757        self.newline();
758    }
759
760    // ─── Const ────────────────────────────────────────────────────────────
761
762    fn format_const_decl(&mut self, decl: &ConstDecl) {
763        self.format_annotations(&decl.annotations);
764        self.push_indent();
765        self.format_visibility(decl.visibility);
766        self.push("const ");
767        self.push(&decl.name.name);
768        self.push(": ");
769        self.format_type_expr(&decl.ty);
770        self.push(" = ");
771        self.format_expr(&decl.value);
772        self.newline();
773    }
774
775    // ─── Module handle ────────────────────────────────────────────────────
776
777    fn format_module_handle(&mut self, decl: &ModuleHandleDecl) {
778        self.push_indent();
779        self.push("handle ");
780        self.format_type_path(&decl.effect);
781        self.push(" with ");
782        self.format_expr(&decl.handler);
783        self.newline();
784    }
785
786    // ─── Property test ────────────────────────────────────────────────────
787
788    fn format_property_test(&mut self, decl: &PropertyTestDecl) {
789        self.push_indent();
790        self.push("property(\"");
791        self.push(&decl.name);
792        self.push("\") {");
793        self.newline();
794        self.inc_indent();
795        if !decl.bindings.is_empty() {
796            self.push_indent();
797            self.push("forall(");
798            for (i, b) in decl.bindings.iter().enumerate() {
799                if i > 0 {
800                    self.push(", ");
801                }
802                self.push(&b.name.name);
803                self.push(": ");
804                self.format_type_expr(&b.ty);
805            }
806            self.push(") ");
807            self.format_block(&decl.body);
808            self.newline();
809        } else {
810            self.format_block_body(&decl.body);
811        }
812        self.dec_indent();
813        self.push_line("}");
814    }
815
816    // ─── Type expressions ─────────────────────────────────────────────────
817
818    fn format_type_expr(&mut self, ty: &TypeExpr) {
819        match ty {
820            TypeExpr::Named { path, args, .. } => {
821                self.format_type_path(path);
822                if !args.is_empty() {
823                    self.push_char('[');
824                    for (i, arg) in args.iter().enumerate() {
825                        if i > 0 {
826                            self.push(", ");
827                        }
828                        self.format_type_expr(arg);
829                    }
830                    self.push_char(']');
831                }
832            }
833            TypeExpr::Tuple { elems, .. } => {
834                self.push_char('(');
835                for (i, elem) in elems.iter().enumerate() {
836                    if i > 0 {
837                        self.push(", ");
838                    }
839                    self.format_type_expr(elem);
840                }
841                self.push_char(')');
842            }
843            TypeExpr::Function {
844                params,
845                ret,
846                effects,
847                ..
848            } => {
849                self.push("Fn(");
850                for (i, p) in params.iter().enumerate() {
851                    if i > 0 {
852                        self.push(", ");
853                    }
854                    self.format_type_expr(p);
855                }
856                self.push(") -> ");
857                self.format_type_expr(ret);
858                if !effects.is_empty() {
859                    self.push(" with ");
860                    for (i, eff) in effects.iter().enumerate() {
861                        if i > 0 {
862                            self.push(", ");
863                        }
864                        self.format_type_path(eff);
865                    }
866                }
867            }
868            TypeExpr::Optional { inner, .. } => {
869                self.format_type_expr(inner);
870                self.push_char('?');
871            }
872            TypeExpr::SelfType { .. } => {
873                self.push("Self");
874            }
875        }
876    }
877
878    fn format_type_path(&mut self, path: &TypePath) {
879        for (i, seg) in path.segments.iter().enumerate() {
880            if i > 0 {
881                self.push_char('.');
882            }
883            self.push(&seg.name);
884        }
885    }
886
887    // ─── Patterns ─────────────────────────────────────────────────────────
888
889    fn format_pattern(&mut self, pat: &Pattern) {
890        match pat {
891            Pattern::Wildcard { .. } => self.push_char('_'),
892            Pattern::Bind { name, .. } => self.push(&name.name),
893            Pattern::MutBind { name, .. } => {
894                self.push("mut ");
895                self.push(&name.name);
896            }
897            Pattern::Literal { lit, .. } => self.format_literal(lit),
898            Pattern::Constructor { path, fields, .. } => {
899                self.format_type_path(path);
900                self.push_char('(');
901                for (i, f) in fields.iter().enumerate() {
902                    if i > 0 {
903                        self.push(", ");
904                    }
905                    self.format_pattern(f);
906                }
907                self.push_char(')');
908            }
909            Pattern::Record {
910                path, fields, rest, ..
911            } => {
912                self.format_type_path(path);
913                self.push(" { ");
914                for (i, f) in fields.iter().enumerate() {
915                    if i > 0 {
916                        self.push(", ");
917                    }
918                    self.push(&f.name.name);
919                    if let Some(p) = &f.pattern {
920                        self.push(": ");
921                        self.format_pattern(p);
922                    }
923                }
924                if *rest {
925                    if !fields.is_empty() {
926                        self.push(", ");
927                    }
928                    self.push("..");
929                }
930                self.push(" }");
931            }
932            Pattern::Tuple { elems, .. } => {
933                self.push_char('(');
934                for (i, e) in elems.iter().enumerate() {
935                    if i > 0 {
936                        self.push(", ");
937                    }
938                    self.format_pattern(e);
939                }
940                self.push_char(')');
941            }
942            Pattern::List { elems, rest, .. } => {
943                self.push_char('[');
944                for (i, e) in elems.iter().enumerate() {
945                    if i > 0 {
946                        self.push(", ");
947                    }
948                    self.format_pattern(e);
949                }
950                if let Some(r) = rest {
951                    if !elems.is_empty() {
952                        self.push(", ");
953                    }
954                    self.push("..");
955                    self.format_pattern(r);
956                }
957                self.push_char(']');
958            }
959            Pattern::Or { alternatives, .. } => {
960                for (i, alt) in alternatives.iter().enumerate() {
961                    if i > 0 {
962                        self.push(" | ");
963                    }
964                    self.format_pattern(alt);
965                }
966            }
967            Pattern::Range {
968                lo, hi, inclusive, ..
969            } => {
970                self.format_pattern(lo);
971                if *inclusive {
972                    self.push("..=");
973                } else {
974                    self.push("..");
975                }
976                self.format_pattern(hi);
977            }
978            Pattern::Rest { .. } => {
979                self.push("..");
980            }
981        }
982    }
983
984    // ─── Expressions ──────────────────────────────────────────────────────
985
986    fn format_expr(&mut self, expr: &Expr) {
987        match expr {
988            Expr::Literal { lit, .. } => self.format_literal(lit),
989            Expr::Identifier { name, .. } => self.push(&name.name),
990            Expr::Binary {
991                op, left, right, ..
992            } => {
993                self.format_expr_maybe_paren(left, expr);
994                self.push_char(' ');
995                self.push(binop_str(*op));
996                self.push_char(' ');
997                self.format_expr_maybe_paren(right, expr);
998            }
999            Expr::Unary { op, operand, .. } => {
1000                self.push(unaryop_str(*op));
1001                self.format_expr(operand);
1002            }
1003            Expr::Assign {
1004                op, target, value, ..
1005            } => {
1006                self.format_expr(target);
1007                self.push_char(' ');
1008                self.push(assignop_str(*op));
1009                self.push_char(' ');
1010                self.format_expr(value);
1011            }
1012            Expr::Call {
1013                callee,
1014                args,
1015                type_args,
1016                ..
1017            } => {
1018                self.format_expr(callee);
1019                if !type_args.is_empty() {
1020                    self.push_char('[');
1021                    for (i, ta) in type_args.iter().enumerate() {
1022                        if i > 0 {
1023                            self.push(", ");
1024                        }
1025                        self.format_type_expr(ta);
1026                    }
1027                    self.push_char(']');
1028                }
1029                self.push_char('(');
1030                self.format_args(args);
1031                self.push_char(')');
1032            }
1033            Expr::MethodCall {
1034                receiver,
1035                method,
1036                type_args,
1037                args,
1038                ..
1039            } => {
1040                self.format_expr(receiver);
1041                self.push_char('.');
1042                self.push(&method.name);
1043                if !type_args.is_empty() {
1044                    self.push_char('[');
1045                    for (i, ta) in type_args.iter().enumerate() {
1046                        if i > 0 {
1047                            self.push(", ");
1048                        }
1049                        self.format_type_expr(ta);
1050                    }
1051                    self.push_char(']');
1052                }
1053                self.push_char('(');
1054                self.format_args(args);
1055                self.push_char(')');
1056            }
1057            Expr::FieldAccess { object, field, .. } => {
1058                self.format_expr(object);
1059                self.push_char('.');
1060                self.push(&field.name);
1061            }
1062            Expr::Index { object, index, .. } => {
1063                self.format_expr(object);
1064                self.push_char('[');
1065                self.format_expr(index);
1066                self.push_char(']');
1067            }
1068            Expr::Try { expr: inner, .. } => {
1069                self.format_expr(inner);
1070                self.push_char('?');
1071            }
1072            Expr::Lambda { params, body, .. } => {
1073                self.push_char('(');
1074                for (i, p) in params.iter().enumerate() {
1075                    if i > 0 {
1076                        self.push(", ");
1077                    }
1078                    self.format_param(p);
1079                }
1080                self.push(") => ");
1081                self.format_expr(body);
1082            }
1083            Expr::Pipe { left, right, .. } => {
1084                self.format_expr(left);
1085                self.newline();
1086                self.push_indent();
1087                self.push("|> ");
1088                self.format_expr(right);
1089            }
1090            Expr::Compose { left, right, .. } => {
1091                self.format_expr(left);
1092                self.push(" >> ");
1093                self.format_expr(right);
1094            }
1095            Expr::If {
1096                let_pattern,
1097                condition,
1098                then_block,
1099                else_block,
1100                ..
1101            } => {
1102                self.push("if ");
1103                if let Some(pat) = let_pattern {
1104                    self.push("let ");
1105                    self.format_pattern(pat);
1106                    self.push(" = ");
1107                }
1108                self.format_expr(condition);
1109                self.push(" ");
1110                self.format_block(then_block);
1111                if let Some(else_expr) = else_block {
1112                    self.push(" else ");
1113                    match else_expr.as_ref() {
1114                        Expr::If { .. } => {
1115                            self.format_expr(else_expr);
1116                        }
1117                        Expr::Block { block, .. } => {
1118                            self.format_block(block);
1119                        }
1120                        _ => {
1121                            self.format_expr(else_expr);
1122                        }
1123                    }
1124                }
1125            }
1126            Expr::Match {
1127                scrutinee, arms, ..
1128            } => {
1129                self.push("match ");
1130                self.format_expr(scrutinee);
1131                self.push(" {");
1132                self.newline();
1133                self.inc_indent();
1134                for arm in arms {
1135                    self.format_match_arm(arm);
1136                }
1137                self.dec_indent();
1138                self.push_indent();
1139                self.push_char('}');
1140            }
1141            Expr::Loop { body, .. } => {
1142                self.push("loop ");
1143                self.format_block(body);
1144            }
1145            Expr::Block { block, .. } => {
1146                self.format_block(block);
1147            }
1148            Expr::RecordConstruct {
1149                path,
1150                fields,
1151                spread,
1152                ..
1153            } => {
1154                self.format_type_path(path);
1155                self.push(" {");
1156                if fields.is_empty() && spread.is_none() {
1157                    self.push("}");
1158                } else {
1159                    self.newline();
1160                    self.inc_indent();
1161                    for field in fields {
1162                        self.push_indent();
1163                        self.push(&field.name.name);
1164                        if let Some(val) = &field.value {
1165                            self.push(": ");
1166                            self.format_expr(val);
1167                        }
1168                        self.push_char(',');
1169                        self.newline();
1170                    }
1171                    if let Some(spr) = spread {
1172                        self.push_indent();
1173                        self.push("..");
1174                        self.format_expr(&spr.expr);
1175                        self.push_char(',');
1176                        self.newline();
1177                    }
1178                    self.dec_indent();
1179                    self.push_indent();
1180                    self.push_char('}');
1181                }
1182            }
1183            Expr::ListLiteral { elems, .. } => {
1184                self.format_collection('[', ']', elems);
1185            }
1186            Expr::MapLiteral { entries, .. } => {
1187                if entries.is_empty() {
1188                    self.push("{}");
1189                } else {
1190                    self.push("{");
1191                    self.newline();
1192                    self.inc_indent();
1193                    for (k, v) in entries {
1194                        self.push_indent();
1195                        self.format_expr(k);
1196                        self.push(": ");
1197                        self.format_expr(v);
1198                        self.push_char(',');
1199                        self.newline();
1200                    }
1201                    self.dec_indent();
1202                    self.push_indent();
1203                    self.push("}");
1204                }
1205            }
1206            Expr::SetLiteral { elems, .. } => {
1207                if elems.is_empty() {
1208                    self.push("#{}");
1209                } else {
1210                    self.push("#{");
1211                    for (i, e) in elems.iter().enumerate() {
1212                        if i > 0 {
1213                            self.push(", ");
1214                        }
1215                        self.format_expr(e);
1216                    }
1217                    self.push("}");
1218                }
1219            }
1220            Expr::TupleLiteral { elems, .. } => {
1221                self.push_char('(');
1222                for (i, e) in elems.iter().enumerate() {
1223                    if i > 0 {
1224                        self.push(", ");
1225                    }
1226                    self.format_expr(e);
1227                }
1228                self.push_char(')');
1229            }
1230            Expr::Range {
1231                lo, hi, inclusive, ..
1232            } => {
1233                self.format_expr(lo);
1234                if *inclusive {
1235                    self.push("..=");
1236                } else {
1237                    self.push("..");
1238                }
1239                self.format_expr(hi);
1240            }
1241            Expr::Await { expr: inner, .. } => {
1242                self.push("await ");
1243                self.format_expr(inner);
1244            }
1245            Expr::Return { value, .. } => {
1246                self.push("return");
1247                if let Some(val) = value {
1248                    self.push_char(' ');
1249                    self.format_expr(val);
1250                }
1251            }
1252            Expr::Break { value, .. } => {
1253                self.push("break");
1254                if let Some(val) = value {
1255                    self.push_char(' ');
1256                    self.format_expr(val);
1257                }
1258            }
1259            Expr::Continue { .. } => {
1260                self.push("continue");
1261            }
1262            Expr::Unreachable { .. } => {
1263                self.push("unreachable");
1264            }
1265            Expr::Interpolation { parts, .. } => {
1266                self.push_char('"');
1267                for part in parts {
1268                    match part {
1269                        InterpolationPart::Literal(s) => self.push(s),
1270                        InterpolationPart::Expr(e) => {
1271                            self.push("${");
1272                            self.format_expr(e);
1273                            self.push_char('}');
1274                        }
1275                    }
1276                }
1277                self.push_char('"');
1278            }
1279            Expr::Placeholder { .. } => {
1280                self.push_char('_');
1281            }
1282            Expr::Is {
1283                expr, type_expr, ..
1284            } => {
1285                self.format_expr(expr);
1286                self.push(" is ");
1287                self.format_type_expr(type_expr);
1288            }
1289        }
1290    }
1291
1292    /// Format an expression, adding parentheses if needed for precedence.
1293    fn format_expr_maybe_paren(&mut self, inner: &Expr, _parent: &Expr) {
1294        // For now, add parens around binary sub-expressions in binary contexts
1295        // to preserve correctness. A more sophisticated approach would compare
1296        // actual precedence levels.
1297        match inner {
1298            Expr::Binary { .. } | Expr::Assign { .. } => {
1299                // Check if inner is lower or different precedence than parent
1300                // For simplicity, we don't parenthesize same-level ops
1301                self.format_expr(inner);
1302            }
1303            _ => self.format_expr(inner),
1304        }
1305    }
1306
1307    fn format_args(&mut self, args: &[Arg]) {
1308        for (i, arg) in args.iter().enumerate() {
1309            if i > 0 {
1310                self.push(", ");
1311            }
1312            if arg.mutable {
1313                self.push("mut ");
1314            }
1315            if let Some(label) = &arg.label {
1316                self.push(&label.name);
1317                self.push(": ");
1318            }
1319            self.format_expr(&arg.value);
1320        }
1321    }
1322
1323    fn format_collection(&mut self, open: char, close: char, elems: &[Expr]) {
1324        self.push_char(open);
1325        // Estimate total length to decide single vs multi-line
1326        let total_est: usize = elems.iter().map(|e| self.estimate_expr_len(e) + 2).sum();
1327        if total_est + self.indent * INDENT_WIDTH <= SOFT_LIMIT || elems.is_empty() {
1328            for (i, e) in elems.iter().enumerate() {
1329                if i > 0 {
1330                    self.push(", ");
1331                }
1332                self.format_expr(e);
1333            }
1334        } else {
1335            self.newline();
1336            self.inc_indent();
1337            for e in elems {
1338                self.push_indent();
1339                self.format_expr(e);
1340                self.push_char(',');
1341                self.newline();
1342            }
1343            self.dec_indent();
1344            self.push_indent();
1345        }
1346        self.push_char(close);
1347    }
1348
1349    fn format_literal(&mut self, lit: &Literal) {
1350        match lit {
1351            Literal::Int(s) => self.push(s),
1352            Literal::Float(s) => self.push(s),
1353            Literal::Bool(b) => self.push(if *b { "true" } else { "false" }),
1354            Literal::Char(s) => {
1355                self.push_char('\'');
1356                self.push(s);
1357                self.push_char('\'');
1358            }
1359            Literal::String(s) => {
1360                self.push_char('"');
1361                self.push(&escape_string(s));
1362                self.push_char('"');
1363            }
1364            Literal::Unit => self.push("()"),
1365        }
1366    }
1367
1368    fn format_match_arm(&mut self, arm: &MatchArm) {
1369        self.emit_comments_before(arm.span.start);
1370        self.push_indent();
1371        self.format_pattern(&arm.pattern);
1372        if let Some(guard) = &arm.guard {
1373            self.push(" if ");
1374            self.format_expr(guard);
1375        }
1376        self.push(" => ");
1377        match &arm.body {
1378            Expr::Block { block, .. } => {
1379                self.format_block(block);
1380                self.push_char(',');
1381            }
1382            _ => {
1383                self.format_expr(&arm.body);
1384                self.push_char(',');
1385            }
1386        }
1387        self.newline();
1388    }
1389
1390    // ─── Blocks ───────────────────────────────────────────────────────────
1391
1392    fn format_block(&mut self, block: &Block) {
1393        self.push_char('{');
1394        if block.stmts.is_empty() && block.tail.is_none() {
1395            self.push_char('}');
1396            return;
1397        }
1398        self.newline();
1399        self.inc_indent();
1400        self.format_block_body(block);
1401        self.dec_indent();
1402        self.push_indent();
1403        self.push_char('}');
1404    }
1405
1406    fn format_block_body(&mut self, block: &Block) {
1407        for stmt in &block.stmts {
1408            self.format_stmt(stmt);
1409        }
1410        if let Some(tail) = &block.tail {
1411            self.emit_comments_before(tail.span().start);
1412            self.push_indent();
1413            self.format_expr(tail);
1414            self.newline();
1415        }
1416    }
1417
1418    // ─── Statements ───────────────────────────────────────────────────────
1419
1420    fn format_stmt(&mut self, stmt: &Stmt) {
1421        match stmt {
1422            Stmt::Let(let_stmt) => self.format_let_stmt(let_stmt),
1423            Stmt::Expr(expr) => {
1424                self.emit_comments_before(expr.span().start);
1425                self.push_indent();
1426                self.format_expr(expr);
1427                self.newline();
1428            }
1429            Stmt::For(for_loop) => self.format_for_loop(for_loop),
1430            Stmt::While(while_loop) => self.format_while_loop(while_loop),
1431            Stmt::Loop(loop_stmt) => self.format_loop_stmt(loop_stmt),
1432            Stmt::Guard(guard) => self.format_guard_stmt(guard),
1433            Stmt::Handling(handling) => self.format_handling_block(handling),
1434            Stmt::Empty => {}
1435        }
1436    }
1437
1438    fn format_let_stmt(&mut self, stmt: &LetStmt) {
1439        self.emit_comments_before(stmt.span.start);
1440        self.push_indent();
1441        self.push("let ");
1442        self.format_pattern(&stmt.pattern);
1443        if let Some(ty) = &stmt.ty {
1444            self.push(": ");
1445            self.format_type_expr(ty);
1446        }
1447        self.push(" = ");
1448        self.format_expr(&stmt.value);
1449        self.newline();
1450    }
1451
1452    fn format_for_loop(&mut self, stmt: &ForLoop) {
1453        self.emit_comments_before(stmt.span.start);
1454        self.push_indent();
1455        self.push("for ");
1456        self.format_pattern(&stmt.pattern);
1457        self.push(" in ");
1458        self.format_expr(&stmt.iterable);
1459        self.push(" ");
1460        self.format_block(&stmt.body);
1461        self.newline();
1462    }
1463
1464    fn format_while_loop(&mut self, stmt: &WhileLoop) {
1465        self.emit_comments_before(stmt.span.start);
1466        self.push_indent();
1467        self.push("while ");
1468        self.format_expr(&stmt.condition);
1469        self.push(" ");
1470        self.format_block(&stmt.body);
1471        self.newline();
1472    }
1473
1474    fn format_loop_stmt(&mut self, stmt: &LoopStmt) {
1475        self.emit_comments_before(stmt.span.start);
1476        self.push_indent();
1477        self.push("loop ");
1478        self.format_block(&stmt.body);
1479        self.newline();
1480    }
1481
1482    fn format_guard_stmt(&mut self, stmt: &GuardStmt) {
1483        self.emit_comments_before(stmt.span.start);
1484        self.push_indent();
1485        if let Some(pat) = &stmt.let_pattern {
1486            self.push("guard (let ");
1487            self.format_pattern(pat);
1488            self.push(" = ");
1489            self.format_expr(&stmt.condition);
1490            self.push(") else ");
1491        } else {
1492            self.push("guard ");
1493            self.format_expr(&stmt.condition);
1494            self.push(" else ");
1495        }
1496        self.format_block(&stmt.else_block);
1497        self.newline();
1498    }
1499
1500    fn format_handling_block(&mut self, stmt: &HandlingBlock) {
1501        self.emit_comments_before(stmt.span.start);
1502        self.push_indent();
1503        self.push("handling (");
1504        for (i, h) in stmt.handlers.iter().enumerate() {
1505            if i > 0 {
1506                self.push(", ");
1507            }
1508            self.format_type_path(&h.effect);
1509            self.push(" with ");
1510            self.format_expr(&h.handler);
1511        }
1512        self.push(") ");
1513        self.format_block(&stmt.body);
1514        self.newline();
1515    }
1516}
1517
1518// ─── Helper functions ─────────────────────────────────────────────────────
1519
1520fn binop_str(op: BinOp) -> &'static str {
1521    match op {
1522        BinOp::Add => "+",
1523        BinOp::Sub => "-",
1524        BinOp::Mul => "*",
1525        BinOp::Div => "/",
1526        BinOp::Rem => "%",
1527        BinOp::Pow => "**",
1528        BinOp::Eq => "==",
1529        BinOp::Ne => "!=",
1530        BinOp::Lt => "<",
1531        BinOp::Le => "<=",
1532        BinOp::Gt => ">",
1533        BinOp::Ge => ">=",
1534        BinOp::And => "&&",
1535        BinOp::Or => "||",
1536        BinOp::BitAnd => "&",
1537        BinOp::BitOr => "|",
1538        BinOp::BitXor => "^",
1539        BinOp::Compose => ">>",
1540        BinOp::Is => "is",
1541    }
1542}
1543
1544fn unaryop_str(op: UnaryOp) -> &'static str {
1545    match op {
1546        UnaryOp::Neg => "-",
1547        UnaryOp::Not => "!",
1548        UnaryOp::BitNot => "~",
1549    }
1550}
1551
1552fn assignop_str(op: AssignOp) -> &'static str {
1553    match op {
1554        AssignOp::Assign => "=",
1555        AssignOp::AddAssign => "+=",
1556        AssignOp::SubAssign => "-=",
1557        AssignOp::MulAssign => "*=",
1558        AssignOp::DivAssign => "/=",
1559        AssignOp::RemAssign => "%=",
1560    }
1561}
1562
1563/// Import category for sorting: 0=core, 1=std, 2=external, 3=local.
1564fn import_category(import: &ImportDecl) -> u8 {
1565    let first = import
1566        .path
1567        .segments
1568        .first()
1569        .map(|s| s.name.as_str())
1570        .unwrap_or("");
1571    match first {
1572        "Core" => 0,
1573        "Std" => 1,
1574        _ => {
1575            // Uppercase initial = external package, lowercase = local module
1576            if first.starts_with(|c: char| c.is_uppercase()) {
1577                2
1578            } else {
1579                3
1580            }
1581        }
1582    }
1583}
1584
1585fn import_path_str(import: &ImportDecl) -> String {
1586    import
1587        .path
1588        .segments
1589        .iter()
1590        .map(|s| s.name.as_str())
1591        .collect::<Vec<_>>()
1592        .join(".")
1593}
1594
1595/// Wrap lines that exceed the hard limit (100 chars).
1596///
1597/// For each line longer than `HARD_LIMIT`, finds the best natural break point
1598/// (after commas, before operators, before `.`) and wraps there. Continuation
1599/// lines are indented by the original line's indentation plus 4 extra spaces.
1600fn wrap_long_lines(input: &str) -> String {
1601    let mut output = String::with_capacity(input.len());
1602    for line in input.lines() {
1603        if line.len() <= HARD_LIMIT {
1604            output.push_str(line);
1605            output.push('\n');
1606        } else {
1607            wrap_single_line(line, &mut output);
1608        }
1609    }
1610    output
1611}
1612
1613/// Wrap a single line that exceeds the hard limit.
1614fn wrap_single_line(line: &str, output: &mut String) {
1615    let base_indent = line.len() - line.trim_start().len();
1616    let continuation_indent = base_indent + INDENT_WIDTH * 2;
1617    let cont_prefix: String = " ".repeat(continuation_indent);
1618
1619    let mut remaining = line.to_string();
1620    while remaining.len() > HARD_LIMIT {
1621        if let Some(pos) = find_break_point(&remaining, HARD_LIMIT) {
1622            let (before, after) = split_at_break(&remaining, pos);
1623            output.push_str(&before);
1624            output.push('\n');
1625            remaining = format!("{cont_prefix}{after}");
1626        } else {
1627            // No natural break point found — emit as-is to avoid infinite loop
1628            break;
1629        }
1630    }
1631    output.push_str(&remaining);
1632    output.push('\n');
1633}
1634
1635/// Find the best break position at or before `limit` in `line`.
1636///
1637/// Prefers breaking at natural points:
1638/// - After `, ` (position after the space)
1639/// - Before binary operators like ` + `, ` - `, ` && `, ` || `, etc.
1640/// - Before `.` in method chains
1641///
1642/// Returns the byte offset where the break should occur (content before this
1643/// offset goes on the current line, content from this offset goes on the next).
1644fn find_break_point(line: &str, limit: usize) -> Option<usize> {
1645    if line.len() <= limit {
1646        return None;
1647    }
1648
1649    // Search window: look backwards from limit to find natural break points.
1650    // We look within the line up to `limit` chars.
1651    let search_end = limit.min(line.len());
1652
1653    // Track the best candidate break position
1654    let mut best: Option<usize> = None;
1655
1656    // Scan for break candidates within the limit.
1657    // We prefer breaks closer to the limit (rightmost).
1658    let bytes = line.as_bytes();
1659
1660    // 1. After ", " — break after the space (next content starts new line)
1661    for i in 0..search_end.saturating_sub(1) {
1662        if bytes[i] == b',' && i + 1 < search_end && bytes[i + 1] == b' ' {
1663            let candidate = i + 2; // break after ", "
1664            if candidate <= limit {
1665                best = Some(candidate);
1666            }
1667        }
1668    }
1669
1670    // 2. Before binary operators: " + ", " - ", " * ", " / ", " % ",
1671    //    " && ", " || ", " == ", " != ", " <= ", " >= ", " < ", " > ",
1672    //    " = ", " => ", " |> ", " >> "
1673    let op_patterns: &[&str] = &[
1674        " && ", " || ", " == ", " != ", " <= ", " >= ", " |> ", " >> ", " => ", " + ", " - ",
1675        " * ", " / ", " % ", " < ", " > ", " = ",
1676    ];
1677    for pat in op_patterns {
1678        // Find all occurrences within the search window
1679        let mut start = 0;
1680        while let Some(pos) = line[start..search_end].find(pat) {
1681            let abs_pos = start + pos;
1682            // Break before the operator (at the space before it)
1683            let candidate = abs_pos + 1; // after the leading space, break before op
1684            if candidate <= limit
1685                && candidate > 0
1686                && (best.is_none() || candidate > best.unwrap_or(0))
1687            {
1688                best = Some(candidate);
1689            }
1690            start = abs_pos + 1;
1691        }
1692    }
1693
1694    // 3. Before "." (method chain) — but not ".." (range)
1695    for i in 1..search_end {
1696        if bytes[i] == b'.' && (i + 1 >= line.len() || bytes[i + 1] != b'.') && bytes[i - 1] != b'.'
1697        {
1698            let candidate = i; // break before "."
1699            if candidate <= limit
1700                && candidate > 0
1701                && (best.is_none() || candidate > best.unwrap_or(0))
1702            {
1703                best = Some(candidate);
1704            }
1705        }
1706    }
1707
1708    // Only accept break if it's past the indentation
1709    let content_start = line.len() - line.trim_start().len();
1710    best.filter(|&pos| pos > content_start)
1711}
1712
1713/// Split a line at a break position, trimming trailing whitespace from the
1714/// first part and leading whitespace from the second.
1715fn split_at_break(line: &str, pos: usize) -> (String, String) {
1716    let before = line[..pos].trim_end().to_string();
1717    let after = line[pos..].trim_start().to_string();
1718    (before, after)
1719}
1720
1721/// Escape special characters in a string literal.
1722fn escape_string(s: &str) -> String {
1723    let mut out = String::with_capacity(s.len());
1724    for c in s.chars() {
1725        match c {
1726            '"' => out.push_str("\\\""),
1727            '\\' => out.push_str("\\\\"),
1728            '\n' => out.push_str("\\n"),
1729            '\r' => out.push_str("\\r"),
1730            '\t' => out.push_str("\\t"),
1731            _ => out.push(c),
1732        }
1733    }
1734    out
1735}