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 self.neverbreak();
42 } else {
43 if !self.print_trailing_comment(span.hi(), None) {
44 self.space_if_not_bol();
45 }
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.split_once(['e', 'E']).unwrap_or((source, ""))
98 } else {
99 (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
605 .peek_comment()
606 .and_then(|cmnt| (cmnt.span.hi() < block_lo).then_some((cmnt.span, cmnt.style)))
607 {
608 Some((span, style)) => {
609 if enabled {
610 if !self.inline_config.is_disabled(span) || style.is_isolated() {
612 self.cursor.advance_to(span.lo(), true);
613 self.break_offset(SIZE_INFINITY as usize, offset);
614 }
615 if let Some(cmnt) = self.print_comments(
616 block_lo,
617 CommentConfig::skip_leading_ws(false).offset(offset),
618 ) && !cmnt.is_mixed()
619 && !cmnt.is_blank()
620 {
621 self.s.offset(offset);
622 }
623 } else if style.is_isolated() {
624 self.print_sep_unhandled(Separator::Hardbreak);
625 self.s.offset(offset);
626 }
627 }
628 None => {
629 if enabled {
630 self.zerobreak();
631 self.s.offset(offset);
632 } else if self.cursor.enabled {
633 self.print_sep_unhandled(Separator::Space);
634 self.s.offset(offset);
635 self.cursor.advance_to(block_lo, true);
636 }
637 }
638 }
639 self.s.cbox(self.ind);
640 }
641 _ => {
642 self.print_word("{");
643 self.s.cbox(self.ind);
644 if !self.handle_span(self.cursor.span(block_lo), false)
645 && self
646 .print_comments(block_lo, CommentConfig::default())
647 .is_none_or(|cmnt| cmnt.is_mixed())
648 {
649 self.hardbreak_if_nonempty();
650 }
651 }
652 }
653
654 for (i, stmt) in block.iter().enumerate() {
656 let is_last = i == block.len() - 1;
657 print(self, stmt);
658
659 let is_disabled = self.inline_config.is_disabled(get_block_span(stmt));
660 let mut next_enabled = false;
661 let mut next_lo = None;
662 if !is_last {
663 let next_span = get_block_span(&block[i + 1]);
664 next_enabled = !self.inline_config.is_disabled(next_span);
665 next_lo =
666 self.peek_comment_before(next_span.lo()).is_none().then_some(next_span.lo());
667 }
668
669 if !is_disabled
671 && next_enabled
672 && (!is_last
673 || self.peek_comment_before(pos_hi).is_some_and(|cmnt| cmnt.style.is_mixed()))
674 {
675 self.hardbreak_if_not_bol();
676 continue;
677 }
678 if is_disabled
681 && next_enabled
682 && let Some(next_lo) = next_lo
683 && self
684 .peek_comment_before(next_lo)
685 .is_none_or(|cmnt| self.inline_config.is_disabled(cmnt.span))
686 {
687 self.hardbreak_if_not_bol()
688 }
689 }
690
691 self.print_comments(
692 pos_hi,
693 CommentConfig::skip_trailing_ws().mixed_no_break().mixed_prev_space(),
694 );
695 if !block_format.breaks() {
696 if !self.last_token_is_break() {
697 self.hardbreak();
698 }
699 self.s.offset(-self.ind);
700 }
701 self.end();
702 if block_format.with_braces() {
703 self.print_word("}");
704 }
705
706 self.block_depth -= 1;
708 }
709
710 fn print_single_line_block<T: Debug>(
711 &mut self,
712 block: &'ast [T],
713 block_format: BlockFormat,
714 mut print: impl FnMut(&mut Self, &'ast T),
715 mut get_block_span: impl FnMut(&'ast T) -> Span,
716 ) {
717 self.s.cbox(self.ind);
718
719 match block_format {
720 BlockFormat::Compact(true) => {
721 self.scan_break(BreakToken { pre_break: Some("{"), ..Default::default() });
722 print(self, &block[0]);
723 self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default());
724 self.s.scan_break(BreakToken { post_break: Some("}"), ..Default::default() });
725 self.s.offset(-self.ind);
726 }
727 _ => {
728 self.word("{");
729 self.space();
730 print(self, &block[0]);
731 self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default());
732 self.space_if_not_bol();
733 self.s.offset(-self.ind);
734 self.word("}");
735 }
736 }
737
738 self.end();
739 }
740
741 fn print_empty_block(&mut self, block_format: BlockFormat, pos_hi: BytePos) {
742 let has_braces = block_format.with_braces();
743
744 if self.peek_comment_before(pos_hi).is_none_or(|c| c.style.is_trailing()) {
746 if self.config.bracket_spacing {
747 if has_braces {
748 self.word("{ }");
749 } else {
750 self.nbsp();
751 }
752 } else if has_braces {
753 self.word("{}");
754 }
755 self.print_comments(pos_hi, CommentConfig::skip_ws());
756 return;
757 }
758
759 if has_braces {
761 self.word("{");
762 }
763 let mut offset = 0;
764 if let BlockFormat::NoBraces(Some(off)) = block_format {
765 offset = off;
766 }
767 self.print_comments(
768 pos_hi,
769 self.cmnt_config().offset(offset).mixed_no_break().mixed_prev_space().mixed_post_nbsp(),
770 );
771 self.print_comments(
772 pos_hi,
773 CommentConfig::default().mixed_no_break().mixed_prev_space().mixed_post_nbsp(),
774 );
775 if has_braces {
776 self.word("}");
777 }
778 }
779}
780
781#[derive(Debug, Clone, Copy, PartialEq, Eq)]
783pub(crate) struct ListFormat {
784 kind: ListFormatKind,
786 no_ind: bool,
788 break_single: bool,
790 breaks_cmnts: bool,
792 with_space: bool,
794 with_delimiters: bool,
796}
797
798#[derive(Debug, Clone, Copy, PartialEq, Eq)]
800pub(crate) enum ListFormatKind {
801 AlwaysBreak,
803 Consistent,
805 Compact,
807 Inline,
809 Yul { sym_prev: Option<&'static str>, sym_post: Option<&'static str> },
811}
812
813impl Default for ListFormat {
814 fn default() -> Self {
815 Self {
816 kind: ListFormatKind::Consistent,
817 no_ind: false,
818 break_single: false,
819 breaks_cmnts: false,
820 with_space: false,
821 with_delimiters: true,
822 }
823 }
824}
825
826impl ListFormat {
827 pub(crate) fn prev_symbol(&self) -> Option<&'static str> {
829 if let ListFormatKind::Yul { sym_prev, .. } = self.kind { sym_prev } else { None }
830 }
831
832 pub(crate) fn post_symbol(&self) -> Option<&'static str> {
833 if let ListFormatKind::Yul { sym_post, .. } = self.kind { sym_post } else { None }
834 }
835
836 pub(crate) fn is_consistent(&self) -> bool {
837 matches!(self.kind, ListFormatKind::Consistent)
838 }
839
840 pub(crate) fn is_compact(&self) -> bool {
841 matches!(self.kind, ListFormatKind::Compact)
842 }
843
844 pub(crate) fn is_inline(&self) -> bool {
845 matches!(self.kind, ListFormatKind::Inline)
846 }
847
848 pub(crate) fn breaks_with_comments(&self) -> bool {
849 self.breaks_cmnts
850 }
851
852 pub(crate) fn inline() -> Self {
854 Self { kind: ListFormatKind::Inline, ..Default::default() }
855 }
856
857 pub(crate) fn consistent() -> Self {
858 Self { kind: ListFormatKind::Consistent, ..Default::default() }
859 }
860
861 pub(crate) fn compact() -> Self {
862 Self { kind: ListFormatKind::Compact, ..Default::default() }
863 }
864
865 pub(crate) fn always_break() -> Self {
866 Self {
867 kind: ListFormatKind::AlwaysBreak,
868 breaks_cmnts: true,
869 break_single: true,
870 with_delimiters: true,
871 ..Default::default()
872 }
873 }
874
875 pub(crate) fn yul(sym_prev: Option<&'static str>, sym_post: Option<&'static str>) -> Self {
876 Self {
877 kind: ListFormatKind::Yul { sym_prev, sym_post },
878 breaks_cmnts: true,
879 with_delimiters: true,
880 ..Default::default()
881 }
882 }
883
884 pub(crate) fn without_ind(mut self, without: bool) -> Self {
885 if !matches!(self.kind, ListFormatKind::Inline) {
886 self.no_ind = without;
887 }
888 self
889 }
890
891 pub(crate) fn break_single(mut self, value: bool) -> Self {
892 if !matches!(self.kind, ListFormatKind::Inline) {
893 self.break_single = value;
894 }
895 self
896 }
897
898 pub(crate) fn break_cmnts(mut self) -> Self {
899 if !matches!(self.kind, ListFormatKind::Inline) {
900 self.breaks_cmnts = true;
901 }
902 self
903 }
904
905 pub(crate) fn with_space(mut self) -> Self {
906 if !matches!(self.kind, ListFormatKind::Inline) {
907 self.with_space = true;
908 }
909 self
910 }
911
912 pub(crate) fn with_delimiters(mut self, with: bool) -> Self {
913 if matches!(self.kind, ListFormatKind::Compact | ListFormatKind::Consistent) {
914 self.with_delimiters = with;
915 }
916 self
917 }
918
919 pub(crate) fn print_break(&self, soft: bool, elems: usize, p: &mut Printer) {
921 match self.kind {
922 ListFormatKind::Inline => p.nbsp(), ListFormatKind::AlwaysBreak if elems > 1 || (self.break_single && elems == 1) => {
924 p.hardbreak()
925 }
926 _ => {
927 if soft && !self.with_space {
928 p.zerobreak();
929 } else {
930 p.space();
931 }
932 }
933 }
934 }
935}
936
937#[derive(Debug, Clone, Copy, PartialEq, Eq)]
939#[expect(dead_code)]
940pub(crate) enum BlockFormat {
941 Regular,
942 Compact(bool),
945 NoBraces(Option<isize>),
948}
949
950impl BlockFormat {
951 pub(crate) fn with_braces(&self) -> bool {
952 !matches!(self, Self::NoBraces(_))
953 }
954 pub(crate) fn breaks(&self) -> bool {
955 matches!(self, Self::NoBraces(None))
956 }
957
958 pub(crate) fn attempt_single_line(&self) -> bool {
959 matches!(self, Self::Compact(_))
960 }
961}