1use super::{CommentConfig, Separator, State};
2use crate::pp::{BreakToken, Printer, SIZE_INFINITY};
3use foundry_common::iter::IterDelimited;
4use foundry_config::fmt as config;
5use itertools::{Either, Itertools};
6use solar::parse::{
7 Cursor,
8 ast::{self, Span},
9 interface::BytePos,
10};
11use std::{borrow::Cow, fmt::Debug};
12
13pub(crate) trait LitExt<'ast> {
14 fn is_str_concatenation(&self) -> bool;
15}
16
17impl<'ast> LitExt<'ast> for ast::Lit<'ast> {
18 fn is_str_concatenation(&self) -> bool {
20 if let ast::LitKind::Str(_, _, parts) = &self.kind { !parts.is_empty() } else { false }
21 }
22}
23
24impl<'ast> State<'_, 'ast> {
26 pub(super) fn print_lit_inner(&mut self, lit: &'ast ast::Lit<'ast>, is_yul: bool) {
27 let ast::Lit { span, symbol, ref kind } = *lit;
28 if self.handle_span(span, false) {
29 return;
30 }
31
32 match *kind {
33 ast::LitKind::Str(kind, ..) => {
34 self.s.ibox(0);
35 for (pos, (span, symbol)) in lit.literals().delimited() {
36 if !self.handle_span(span, false) {
37 let quote_pos = span.lo() + kind.prefix().len() as u32;
38 self.print_str_lit(kind, quote_pos, symbol.as_str());
39 }
40 if !pos.is_last {
41 if !self.print_trailing_comment(span.hi(), None) {
42 self.space_if_not_bol();
43 }
44 } else {
45 self.neverbreak();
46 }
47 }
48 self.end();
49 }
50 ast::LitKind::Number(_) | ast::LitKind::Rational(_) => {
51 self.print_num_literal(symbol.as_str(), is_yul);
52 }
53 ast::LitKind::Address(value) => self.word(value.to_string()),
54 ast::LitKind::Bool(value) => self.word(if value { "true" } else { "false" }),
55 ast::LitKind::Err(_) => self.word(symbol.to_string()),
56 }
57 }
58
59 fn print_num_literal(&mut self, source: &str, is_yul: bool) {
60 fn strip_underscores_if(b: bool, s: &str) -> Cow<'_, str> {
61 if b && s.contains('_') { Cow::Owned(s.replace('_', "")) } else { Cow::Borrowed(s) }
62 }
63
64 fn add_underscores(
65 out: &mut String,
66 config: config::NumberUnderscore,
67 string: &str,
68 is_dec: bool,
69 is_yul: bool,
70 reversed: bool,
71 ) {
72 if !config.is_thousands() || !is_dec || is_yul || string.len() < 5 {
76 out.push_str(string);
77 return;
78 }
79
80 let chunks = if reversed {
81 Either::Left(string.as_bytes().chunks(3))
82 } else {
83 Either::Right(string.as_bytes().rchunks(3).rev())
84 }
85 .map(|chunk| std::str::from_utf8(chunk).unwrap());
86 for chunk in Itertools::intersperse(chunks, "_") {
87 out.push_str(chunk);
88 }
89 }
90
91 debug_assert!(source.is_ascii(), "{source:?}");
92
93 let config = self.config.number_underscore;
94 let is_dec = !["0x", "0b", "0o"].iter().any(|prefix| source.starts_with(prefix));
95
96 let (val, exp) = if !is_dec {
97 (source, "")
98 } else {
99 source.split_once(['e', 'E']).unwrap_or((source, ""))
100 };
101 let (val, fract) = val.split_once('.').unwrap_or((val, ""));
102
103 let strip_underscores = !config.is_preserve() || is_yul;
104 let mut val = &strip_underscores_if(strip_underscores, val)[..];
105 let mut exp = &strip_underscores_if(strip_underscores, exp)[..];
106 let mut fract = &strip_underscores_if(strip_underscores, fract)[..];
107
108 let mut exp_sign = "";
110 if is_dec {
111 val = val.trim_start_matches('0');
112 fract = fract.trim_end_matches('0');
113 (exp_sign, exp) =
114 if let Some(exp) = exp.strip_prefix('-') { ("-", exp) } else { ("", exp) };
115 exp = exp.trim_start_matches('0');
116 }
117
118 let mut out = String::with_capacity(source.len() * 2);
119 if val.is_empty() {
120 out.push('0');
121 } else {
122 add_underscores(&mut out, config, val, is_dec, is_yul, false);
123 }
124 if source.contains('.') {
125 out.push('.');
126 match (fract.is_empty(), exp.is_empty()) {
127 (false, false) => out.push_str(fract),
129 (false, true) => add_underscores(&mut out, config, fract, is_dec, is_yul, true),
131 (true, _) => out.push('0'),
133 };
134 }
135 if !exp.is_empty() {
136 out.push('e');
137 out.push_str(exp_sign);
138 add_underscores(&mut out, config, exp, is_dec, is_yul, false);
139 }
140
141 self.word(out);
142 }
143
144 pub(super) fn print_str_lit(&mut self, kind: ast::StrKind, quote_pos: BytePos, s: &str) {
146 self.print_comments(quote_pos, CommentConfig::default());
147 let s = self.str_lit_to_string(kind, quote_pos, s);
148 self.word(s);
149 }
150
151 fn str_lit_to_string(&self, kind: ast::StrKind, quote_pos: BytePos, s: &str) -> String {
153 let prefix = kind.prefix();
154 let quote = match self.config.quote_style {
155 config::QuoteStyle::Double => '\"',
156 config::QuoteStyle::Single => '\'',
157 config::QuoteStyle::Preserve => self.char_at(quote_pos).unwrap_or_default(),
158 };
159 debug_assert!(matches!(quote, '\"' | '\''), "{quote:?}");
160 let s = solar::parse::interface::data_structures::fmt::from_fn(move |f| {
161 if matches!(kind, ast::StrKind::Hex) {
162 match self.config.hex_underscore {
163 config::HexUnderscore::Preserve => {}
164 config::HexUnderscore::Remove | config::HexUnderscore::Bytes => {
165 let mut clean = s.to_string().replace('_', "");
166 if matches!(self.config.hex_underscore, config::HexUnderscore::Bytes) {
167 clean =
168 clean.chars().chunks(2).into_iter().map(|c| c.format("")).join("_");
169 }
170 return f.write_str(&clean);
171 }
172 };
173 }
174 f.write_str(s)
175 });
176 let mut s = format!("{prefix}{quote}{s}{quote}");
177
178 #[allow(unstable_name_collisions)]
180 if Cursor::new(&s).exactly_one().is_err() {
181 let other_quote = if quote == '\"' { '\'' } else { '\"' };
182 {
183 let s = unsafe { s.as_bytes_mut() };
184 s[prefix.len()] = other_quote as u8;
185 s[s.len() - 1] = other_quote as u8;
186 }
187 debug_assert!(Cursor::new(&s).exactly_one().map(|_| true).unwrap());
188 }
189
190 s
191 }
192
193 pub(super) fn print_tuple_empty(&mut self, pos_lo: BytePos, pos_hi: BytePos) {
194 if self.handle_span(Span::new(pos_lo, pos_hi), true) {
195 return;
196 }
197
198 self.print_inside_parens(|state| {
199 state.s.cbox(state.ind);
200 if let Some(cmnt) =
201 state.print_comments(pos_hi, CommentConfig::skip_ws().mixed_prev_space())
202 {
203 if cmnt.is_mixed() {
204 state.s.offset(-state.ind);
205 } else {
206 state.break_offset_if_not_bol(0, -state.ind, false);
207 }
208 }
209 state.end();
210 });
211 }
212
213 pub(super) fn print_tuple<'a, T, P, S>(
214 &mut self,
215 values: &'a [T],
216 pos_lo: BytePos,
217 pos_hi: BytePos,
218 mut print: P,
219 mut get_span: S,
220 format: ListFormat,
221 ) where
222 P: FnMut(&mut Self, &'a T),
223 S: FnMut(&T) -> Span,
224 {
225 if self.handle_span(Span::new(pos_lo, pos_hi), true) {
226 return;
227 }
228
229 if values.is_empty() {
230 self.print_tuple_empty(pos_lo, pos_hi);
231 return;
232 }
233
234 if !(values.len() == 1 && format.is_inline()) {
235 self.print_inside_parens(|state| {
237 state.commasep(values, pos_lo, pos_hi, print, get_span, format)
238 });
239 return;
240 }
241
242 self.print_inside_parens(|state| {
244 let span = get_span(&values[0]);
245 state.s.cbox(state.ind);
246 let mut skip_break = true;
247 if state.peek_comment_before(span.hi()).is_some() {
248 state.hardbreak();
249 skip_break = false;
250 }
251
252 state.print_comments(span.lo(), CommentConfig::skip_ws().mixed_prev_space());
253 print(state, &values[0]);
254
255 if !state.print_trailing_comment(span.hi(), None) && skip_break {
256 state.neverbreak();
257 } else {
258 state.break_offset_if_not_bol(0, -state.ind, false);
259 }
260 state.end();
261 });
262 }
263
264 pub(super) fn print_array<'a, T, P, S>(
265 &mut self,
266 values: &'a [T],
267 span: Span,
268 print: P,
269 get_span: S,
270 ) where
271 P: FnMut(&mut Self, &'a T),
272 S: FnMut(&T) -> Span,
273 {
274 if self.handle_span(span, false) {
275 return;
276 }
277
278 self.print_word("[");
279 self.commasep(values, span.lo(), span.hi(), print, get_span, ListFormat::compact());
280 self.print_word("]");
281 }
282
283 pub(super) fn commasep_opening_logic<T, S>(
284 &mut self,
285 values: &[T],
286 mut get_span: S,
287 format: ListFormat,
288 manual_opening: bool,
289 ) -> bool
290 where
291 S: FnMut(&T) -> Span,
292 {
293 let Some(span) = values.first().map(&mut get_span) else {
294 return false;
295 };
296
297 if span.is_dummy()
300 && let Some(next_pos) = values.get(1).map(|v| get_span(v).lo())
301 && self.peek_comment_before(next_pos).is_some()
302 {
303 return true;
304 }
305
306 if let Some((cmnt_span, cmnt_style)) =
308 self.peek_comment_before(span.lo()).map(|c| (c.span, c.style))
309 {
310 let cmnt_disabled = self.inline_config.is_disabled(cmnt_span);
311 if self.cursor.enabled && cmnt_disabled && cmnt_style.is_isolated() {
313 self.print_sep(Separator::Hardbreak);
314 if !format.with_delimiters {
315 self.s.offset(self.ind);
316 }
317 };
318
319 if manual_opening {
322 self.hardbreak();
323 self.s.offset(self.ind);
324 return true;
325 }
326
327 let cmnt_config = if format.with_delimiters {
328 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space()
329 } else {
330 CommentConfig::skip_ws().no_breaks().mixed_prev_space().offset(self.ind)
331 };
332 if let Some(last_style) = self.print_comments(span.lo(), cmnt_config) {
334 match (cmnt_style.is_mixed(), last_style.is_mixed()) {
335 (true, true) => {
336 if format.breaks_cmnts {
337 self.hardbreak();
338 } else {
339 self.space();
340 }
341 if !format.with_delimiters && !cmnt_disabled {
342 self.s.offset(self.ind);
343 }
344 }
345 (false, true) => {
346 self.nbsp();
347 }
348 (false, false) if !format.with_delimiters && !cmnt_disabled => {
349 self.hardbreak();
350 self.s.offset(self.ind);
351 }
352 _ => {}
353 }
354 }
355 if self.cursor.enabled {
356 self.cursor.advance_to(span.lo(), true);
357 }
358 return true;
359 }
360
361 if self.cursor.enabled {
362 self.cursor.advance_to(span.lo(), true);
363 }
364
365 if !values.is_empty() && !format.with_delimiters {
366 format.print_break(true, values.len(), &mut self.s);
367 self.s.offset(self.ind);
368 return true;
369 }
370
371 false
372 }
373
374 pub(super) fn commasep<'a, T, P, S>(
375 &mut self,
376 values: &'a [T],
377 _pos_lo: BytePos,
378 pos_hi: BytePos,
379 mut print: P,
380 mut get_span: S,
381 format: ListFormat,
382 ) where
383 P: FnMut(&mut Self, &'a T),
384 S: FnMut(&T) -> Span,
385 {
386 if values.is_empty() {
387 return;
388 }
389
390 let has_comments =
393 self.peek_comment_before(get_span(&values[0]).lo()).is_some() ||
395 values.windows(2).any(|w| self.peek_comment_between(get_span(&w[0]).hi(), get_span(&w[1]).lo()).is_some()) ||
397 self.peek_comment_between(get_span(values.last().unwrap()).hi(), pos_hi).is_some();
399
400 let manual_opening =
404 format.is_consistent() && !format.with_delimiters && self.call_with_opts_and_args;
405 let manual_offset = !has_comments && manual_opening;
407
408 let is_single_without_cmnts = values.len() == 1 && !format.break_single && !has_comments;
409 let skip_first_break = if format.with_delimiters || format.is_inline() {
410 self.s.cbox(if format.no_ind { 0 } else { self.ind });
411 if is_single_without_cmnts {
412 true
413 } else {
414 self.commasep_opening_logic(values, &mut get_span, format, manual_opening)
415 }
416 } else {
417 let res = self.commasep_opening_logic(values, &mut get_span, format, manual_opening);
418 if !manual_offset {
419 self.s.cbox(if format.no_ind { 0 } else { self.ind });
420 }
421 res
422 };
423
424 if let Some(sym) = format.prev_symbol() {
425 self.word_space(sym);
426 } else if is_single_without_cmnts && format.with_space {
427 self.nbsp();
428 } else if !skip_first_break && !format.is_inline() {
429 format.print_break(true, values.len(), &mut self.s);
430 if manual_offset {
431 self.s.offset(self.ind);
432 }
433 }
434
435 if format.is_compact() && !(format.breaks_with_comments() && has_comments) {
436 self.s.cbox(0);
437 }
438
439 let mut last_delimiter_break = !format.with_delimiters;
440 let mut skip_last_break =
441 is_single_without_cmnts || !format.with_delimiters || format.is_inline();
442 for (i, value) in values.iter().enumerate() {
443 let is_last = i == values.len() - 1;
444 if self
445 .print_comments(get_span(value).lo(), CommentConfig::skip_ws().mixed_prev_space())
446 .is_some_and(|cmnt| cmnt.is_mixed())
447 && format.breaks_cmnts
448 {
449 self.hardbreak(); }
451
452 if !(is_last && get_span(value).is_dummy()) {
454 print(self, value);
455 }
456
457 let next_span = if is_last { None } else { Some(get_span(&values[i + 1])) };
458 let next_pos = next_span.map(Span::lo).unwrap_or(pos_hi);
459 let cmnt_before_next =
460 self.peek_comment_before(next_pos).map(|cmnt| (cmnt.span, cmnt.style));
461
462 if !is_last {
463 if cmnt_before_next.is_some_and(|(cmnt_span, _)| {
465 let span = self.cursor.span(cmnt_span.lo());
466 self.inline_config.is_disabled(span)
467 && self.sm.span_to_snippet(span).is_ok_and(|snip| !snip.contains(','))
469 }) {
470 self.print_comments(
471 next_pos,
472 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(),
473 );
474 }
475 self.print_word(",");
476 }
477
478 if !is_last
479 && format.breaks_cmnts
480 && cmnt_before_next.is_some_and(|(cmnt_span, cmnt_style)| {
481 let disabled = self.inline_config.is_disabled(cmnt_span);
482 (cmnt_style.is_mixed() && !disabled) || (cmnt_style.is_isolated() && disabled)
483 })
484 {
485 self.hardbreak(); }
487
488 let comment_config = if !is_last || format.with_delimiters {
490 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space()
491 } else {
492 CommentConfig::skip_ws().no_breaks().mixed_prev_space()
493 };
494 let with_trailing = self.print_comments(next_pos, comment_config).is_some();
495
496 if is_last && with_trailing {
497 if self.is_bol_or_only_ind() {
498 self.break_offset_if_not_bol(0, -self.ind, false);
501 } else {
502 self.s.break_offset(SIZE_INFINITY as usize, -self.ind);
503 }
504 skip_last_break = true;
505 last_delimiter_break = false;
506 }
507
508 if let Some(next_span) = next_span
510 && !self.is_bol_or_only_ind()
511 && !self.inline_config.is_disabled(next_span)
512 && !next_span.is_dummy()
513 {
514 format.print_break(false, values.len(), &mut self.s);
515 if manual_offset {
516 self.s.offset(self.ind);
517 }
518 }
519 }
520
521 if format.is_compact() && !(format.breaks_with_comments() && has_comments) {
522 self.end();
523 }
524 if !skip_last_break {
525 if let Some(sym) = format.post_symbol() {
526 format.print_break(false, values.len(), &mut self.s);
527 self.s.offset(-self.ind);
528 self.word(sym);
529 } else {
530 format.print_break(true, values.len(), &mut self.s);
531 self.s.offset(-self.ind);
532 }
533 } else if is_single_without_cmnts && format.with_space {
534 self.nbsp();
535 } else if let Some(sym) = format.post_symbol() {
536 self.nbsp();
537 self.word(sym);
538 }
539
540 if !manual_offset {
541 self.end();
542 }
543 self.cursor.advance_to(pos_hi, true);
544
545 if last_delimiter_break {
546 format.print_break(true, values.len(), &mut self.s);
547 }
548 }
549
550 pub(super) fn print_path(&mut self, path: &'ast ast::PathSlice, consistent_break: bool) {
551 if consistent_break {
552 self.s.cbox(self.ind);
553 } else {
554 self.s.ibox(self.ind);
555 }
556 for (pos, ident) in path.segments().iter().delimited() {
557 self.print_ident(ident);
558 if !pos.is_last {
559 if !self.emit_or_revert {
560 self.zerobreak();
561 }
562 self.word(".");
563 }
564 }
565 self.end();
566 }
567
568 pub(super) fn print_block_inner<T: Debug>(
569 &mut self,
570 block: &'ast [T],
571 block_format: BlockFormat,
572 mut print: impl FnMut(&mut Self, &'ast T),
573 mut get_block_span: impl FnMut(&'ast T) -> Span,
574 pos_hi: BytePos,
575 ) {
576 if block_format.attempt_single_line() && block.len() == 1 {
578 self.print_single_line_block(block, block_format, print, get_block_span);
579 return;
580 }
581
582 if block.is_empty() {
584 self.print_empty_block(block_format, pos_hi);
585 return;
586 }
587
588 self.block_depth += 1;
590
591 let block_lo = get_block_span(&block[0]).lo();
593 match block_format {
594 BlockFormat::NoBraces(None) => {
595 if !self.handle_span(self.cursor.span(block_lo), false) {
596 self.print_comments(block_lo, CommentConfig::default());
597 }
598 self.s.cbox(0);
599 }
600 BlockFormat::NoBraces(Some(offset)) => {
601 let enabled =
602 !self.inline_config.is_disabled(Span::new(block_lo, block_lo + BytePos(1)))
603 && !self.handle_span(self.cursor.span(block_lo), true);
604 match self.peek_comment().and_then(|cmnt| {
605 if cmnt.span.hi() < block_lo { Some((cmnt.span, cmnt.style)) } else { None }
606 }) {
607 Some((span, style)) => {
608 if enabled {
609 if !self.inline_config.is_disabled(span) || style.is_isolated() {
611 self.cursor.advance_to(span.lo(), true);
612 self.break_offset(SIZE_INFINITY as usize, offset);
613 }
614 if let Some(cmnt) = self.print_comments(
615 block_lo,
616 CommentConfig::skip_leading_ws(false).offset(offset),
617 ) && !cmnt.is_mixed()
618 && !cmnt.is_blank()
619 {
620 self.s.offset(offset);
621 }
622 } else if style.is_isolated() {
623 self.print_sep_unhandled(Separator::Hardbreak);
624 self.s.offset(offset);
625 }
626 }
627 None => {
628 if enabled {
629 self.zerobreak();
630 self.s.offset(offset);
631 } else if self.cursor.enabled {
632 self.print_sep_unhandled(Separator::Space);
633 self.s.offset(offset);
634 self.cursor.advance_to(block_lo, true);
635 }
636 }
637 }
638 self.s.cbox(self.ind);
639 }
640 _ => {
641 self.print_word("{");
642 self.s.cbox(self.ind);
643 if !self.handle_span(self.cursor.span(block_lo), false)
644 && self
645 .print_comments(block_lo, CommentConfig::default())
646 .is_none_or(|cmnt| cmnt.is_mixed())
647 {
648 self.hardbreak_if_nonempty();
649 }
650 }
651 }
652
653 for (i, stmt) in block.iter().enumerate() {
655 let is_last = i == block.len() - 1;
656 print(self, stmt);
657
658 let is_disabled = self.inline_config.is_disabled(get_block_span(stmt));
659 let mut next_enabled = false;
660 let mut next_lo = None;
661 if !is_last {
662 let next_span = get_block_span(&block[i + 1]);
663 next_enabled = !self.inline_config.is_disabled(next_span);
664 next_lo =
665 self.peek_comment_before(next_span.lo()).is_none().then_some(next_span.lo());
666 }
667
668 if !is_disabled
670 && next_enabled
671 && (!is_last
672 || self.peek_comment_before(pos_hi).is_some_and(|cmnt| cmnt.style.is_mixed()))
673 {
674 self.hardbreak_if_not_bol();
675 continue;
676 }
677 if is_disabled
680 && next_enabled
681 && let Some(next_lo) = next_lo
682 && self
683 .peek_comment_before(next_lo)
684 .is_none_or(|cmnt| self.inline_config.is_disabled(cmnt.span))
685 {
686 self.hardbreak_if_not_bol()
687 }
688 }
689
690 self.print_comments(
691 pos_hi,
692 CommentConfig::skip_trailing_ws().mixed_no_break().mixed_prev_space(),
693 );
694 if !block_format.breaks() {
695 if !self.last_token_is_break() {
696 self.hardbreak();
697 }
698 self.s.offset(-self.ind);
699 }
700 self.end();
701 if block_format.with_braces() {
702 self.print_word("}");
703 }
704
705 self.block_depth -= 1;
707 }
708
709 fn print_single_line_block<T: Debug>(
710 &mut self,
711 block: &'ast [T],
712 block_format: BlockFormat,
713 mut print: impl FnMut(&mut Self, &'ast T),
714 mut get_block_span: impl FnMut(&'ast T) -> Span,
715 ) {
716 self.s.cbox(self.ind);
717
718 match block_format {
719 BlockFormat::Compact(true) => {
720 self.scan_break(BreakToken { pre_break: Some("{"), ..Default::default() });
721 print(self, &block[0]);
722 self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default());
723 self.s.scan_break(BreakToken { post_break: Some("}"), ..Default::default() });
724 self.s.offset(-self.ind);
725 }
726 _ => {
727 self.word("{");
728 self.space();
729 print(self, &block[0]);
730 self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default());
731 self.space_if_not_bol();
732 self.s.offset(-self.ind);
733 self.word("}");
734 }
735 }
736
737 self.end();
738 }
739
740 fn print_empty_block(&mut self, block_format: BlockFormat, pos_hi: BytePos) {
741 let has_braces = block_format.with_braces();
742
743 if self.peek_comment_before(pos_hi).is_none_or(|c| c.style.is_trailing()) {
745 if self.config.bracket_spacing {
746 if has_braces {
747 self.word("{ }");
748 } else {
749 self.nbsp();
750 }
751 } else if has_braces {
752 self.word("{}");
753 }
754 self.print_comments(pos_hi, CommentConfig::skip_ws());
755 return;
756 }
757
758 if has_braces {
760 self.word("{");
761 }
762 let mut offset = 0;
763 if let BlockFormat::NoBraces(Some(off)) = block_format {
764 offset = off;
765 }
766 self.print_comments(
767 pos_hi,
768 self.cmnt_config().offset(offset).mixed_no_break().mixed_prev_space().mixed_post_nbsp(),
769 );
770 self.print_comments(
771 pos_hi,
772 CommentConfig::default().mixed_no_break().mixed_prev_space().mixed_post_nbsp(),
773 );
774 if has_braces {
775 self.word("}");
776 }
777 }
778}
779
780#[derive(Debug, Clone, Copy, PartialEq, Eq)]
782pub(crate) struct ListFormat {
783 kind: ListFormatKind,
785 no_ind: bool,
787 break_single: bool,
789 breaks_cmnts: bool,
791 with_space: bool,
793 with_delimiters: bool,
795}
796
797#[derive(Debug, Clone, Copy, PartialEq, Eq)]
799pub(crate) enum ListFormatKind {
800 AlwaysBreak,
802 Consistent,
804 Compact,
806 Inline,
808 Yul { sym_prev: Option<&'static str>, sym_post: Option<&'static str> },
810}
811
812impl Default for ListFormat {
813 fn default() -> Self {
814 Self {
815 kind: ListFormatKind::Consistent,
816 no_ind: false,
817 break_single: false,
818 breaks_cmnts: false,
819 with_space: false,
820 with_delimiters: true,
821 }
822 }
823}
824
825impl ListFormat {
826 pub(crate) fn prev_symbol(&self) -> Option<&'static str> {
828 if let ListFormatKind::Yul { sym_prev, .. } = self.kind { sym_prev } else { None }
829 }
830
831 pub(crate) fn post_symbol(&self) -> Option<&'static str> {
832 if let ListFormatKind::Yul { sym_post, .. } = self.kind { sym_post } else { None }
833 }
834
835 pub(crate) fn is_consistent(&self) -> bool {
836 matches!(self.kind, ListFormatKind::Consistent)
837 }
838
839 pub(crate) fn is_compact(&self) -> bool {
840 matches!(self.kind, ListFormatKind::Compact)
841 }
842
843 pub(crate) fn is_inline(&self) -> bool {
844 matches!(self.kind, ListFormatKind::Inline)
845 }
846
847 pub(crate) fn breaks_with_comments(&self) -> bool {
848 self.breaks_cmnts
849 }
850
851 pub(crate) fn inline() -> Self {
853 Self { kind: ListFormatKind::Inline, ..Default::default() }
854 }
855
856 pub(crate) fn consistent() -> Self {
857 Self { kind: ListFormatKind::Consistent, ..Default::default() }
858 }
859
860 pub(crate) fn compact() -> Self {
861 Self { kind: ListFormatKind::Compact, ..Default::default() }
862 }
863
864 pub(crate) fn always_break() -> Self {
865 Self {
866 kind: ListFormatKind::AlwaysBreak,
867 breaks_cmnts: true,
868 break_single: true,
869 with_delimiters: true,
870 ..Default::default()
871 }
872 }
873
874 pub(crate) fn yul(sym_prev: Option<&'static str>, sym_post: Option<&'static str>) -> Self {
875 Self {
876 kind: ListFormatKind::Yul { sym_prev, sym_post },
877 breaks_cmnts: true,
878 with_delimiters: true,
879 ..Default::default()
880 }
881 }
882
883 pub(crate) fn without_ind(mut self, without: bool) -> Self {
884 if !matches!(self.kind, ListFormatKind::Inline) {
885 self.no_ind = without;
886 }
887 self
888 }
889
890 pub(crate) fn break_single(mut self, value: bool) -> Self {
891 if !matches!(self.kind, ListFormatKind::Inline) {
892 self.break_single = value;
893 }
894 self
895 }
896
897 pub(crate) fn break_cmnts(mut self) -> Self {
898 if !matches!(self.kind, ListFormatKind::Inline) {
899 self.breaks_cmnts = true;
900 }
901 self
902 }
903
904 pub(crate) fn with_space(mut self) -> Self {
905 if !matches!(self.kind, ListFormatKind::Inline) {
906 self.with_space = true;
907 }
908 self
909 }
910
911 pub(crate) fn with_delimiters(mut self, with: bool) -> Self {
912 if matches!(self.kind, ListFormatKind::Compact | ListFormatKind::Consistent) {
913 self.with_delimiters = with;
914 }
915 self
916 }
917
918 pub(crate) fn print_break(&self, soft: bool, elems: usize, p: &mut Printer) {
920 match self.kind {
921 ListFormatKind::Inline => p.nbsp(), ListFormatKind::AlwaysBreak if elems > 1 || (self.break_single && elems == 1) => {
923 p.hardbreak()
924 }
925 _ => {
926 if soft && !self.with_space {
927 p.zerobreak();
928 } else {
929 p.space();
930 }
931 }
932 }
933 }
934}
935
936#[derive(Debug, Clone, Copy, PartialEq, Eq)]
938#[expect(dead_code)]
939pub(crate) enum BlockFormat {
940 Regular,
941 Compact(bool),
944 NoBraces(Option<isize>),
947}
948
949impl BlockFormat {
950 pub(crate) fn with_braces(&self) -> bool {
951 !matches!(self, Self::NoBraces(_))
952 }
953 pub(crate) fn breaks(&self) -> bool {
954 matches!(self, Self::NoBraces(None))
955 }
956
957 pub(crate) fn attempt_single_line(&self) -> bool {
958 matches!(self, Self::Compact(_))
959 }
960}