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 skip_break = if state.peek_comment_before(span.hi()).is_some() {
247 state.hardbreak();
248 false
249 } else {
250 true
251 };
252
253 state.print_comments(span.lo(), CommentConfig::skip_ws().mixed_prev_space());
254 print(state, &values[0]);
255
256 if !state.print_trailing_comment(span.hi(), None) && skip_break {
257 state.neverbreak();
258 } else {
259 state.break_offset_if_not_bol(0, -state.ind, false);
260 }
261 state.end();
262 });
263 }
264
265 pub(super) fn print_array<'a, T, P, S>(
266 &mut self,
267 values: &'a [T],
268 span: Span,
269 print: P,
270 get_span: S,
271 ) where
272 P: FnMut(&mut Self, &'a T),
273 S: FnMut(&T) -> Span,
274 {
275 if self.handle_span(span, false) {
276 return;
277 }
278
279 self.print_word("[");
280 self.commasep(values, span.lo(), span.hi(), print, get_span, ListFormat::compact());
281 self.print_word("]");
282 }
283
284 pub(super) fn commasep_opening_logic<T, S>(
285 &mut self,
286 values: &[T],
287 mut get_span: S,
288 format: ListFormat,
289 manual_opening: bool,
290 ) -> bool
291 where
292 S: FnMut(&T) -> Span,
293 {
294 let Some(span) = values.first().map(&mut get_span) else {
295 return false;
296 };
297
298 if span.is_dummy()
301 && let Some(next_pos) = values.get(1).map(|v| get_span(v).lo())
302 && self.peek_comment_before(next_pos).is_some()
303 {
304 return true;
305 }
306
307 if let Some((cmnt_span, cmnt_style)) =
309 self.peek_comment_before(span.lo()).map(|c| (c.span, c.style))
310 {
311 let cmnt_disabled = self.inline_config.is_disabled(cmnt_span);
312 if self.cursor.enabled && cmnt_disabled && cmnt_style.is_isolated() {
314 self.print_sep(Separator::Hardbreak);
315 if !format.with_delimiters {
316 self.s.offset(self.ind);
317 }
318 };
319
320 if manual_opening {
323 self.hardbreak();
324 self.s.offset(self.ind);
325 return true;
326 }
327
328 let cmnt_config = if format.with_delimiters {
329 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space()
330 } else {
331 CommentConfig::skip_ws().no_breaks().mixed_prev_space().offset(self.ind)
332 };
333 if let Some(last_style) = self.print_comments(span.lo(), cmnt_config) {
335 match (cmnt_style.is_mixed(), last_style.is_mixed()) {
336 (true, true) => {
337 if format.breaks_cmnts {
338 self.hardbreak();
339 } else {
340 self.space();
341 }
342 if !format.with_delimiters && !cmnt_disabled {
343 self.s.offset(self.ind);
344 }
345 }
346 (false, true) => {
347 self.nbsp();
348 }
349 (false, false) if !format.with_delimiters && !cmnt_disabled => {
350 self.hardbreak();
351 self.s.offset(self.ind);
352 }
353 _ => {}
354 }
355 }
356 if self.cursor.enabled {
357 self.cursor.advance_to(span.lo(), true);
358 }
359 return true;
360 }
361
362 if self.cursor.enabled {
363 self.cursor.advance_to(span.lo(), true);
364 }
365
366 if !values.is_empty() && !format.with_delimiters {
367 format.print_break(true, values.len(), &mut self.s);
368 self.s.offset(self.ind);
369 return true;
370 }
371
372 false
373 }
374
375 pub(super) fn commasep<'a, T, P, S>(
376 &mut self,
377 values: &'a [T],
378 _pos_lo: BytePos,
379 pos_hi: BytePos,
380 mut print: P,
381 mut get_span: S,
382 format: ListFormat,
383 ) where
384 P: FnMut(&mut Self, &'a T),
385 S: FnMut(&T) -> Span,
386 {
387 if values.is_empty() {
388 return;
389 }
390
391 let has_comments =
394 self.peek_comment_before(get_span(&values[0]).lo()).is_some() ||
396 values.windows(2).any(|w| self.peek_comment_between(get_span(&w[0]).hi(), get_span(&w[1]).lo()).is_some()) ||
398 self.peek_comment_between(get_span(values.last().unwrap()).hi(), pos_hi).is_some();
400
401 let manual_opening =
405 format.is_consistent() && !format.with_delimiters && self.call_with_opts_and_args;
406 let manual_offset = !has_comments && manual_opening;
408
409 let is_single_without_cmnts = values.len() == 1 && !format.break_single && !has_comments;
410 let skip_first_break = if format.with_delimiters || format.is_inline() {
411 self.s.cbox(if format.no_ind { 0 } else { self.ind });
412 if is_single_without_cmnts {
413 true
414 } else {
415 self.commasep_opening_logic(values, &mut get_span, format, manual_opening)
416 }
417 } else {
418 let res = self.commasep_opening_logic(values, &mut get_span, format, manual_opening);
419 if !manual_offset {
420 self.s.cbox(if format.no_ind { 0 } else { self.ind });
421 }
422 res
423 };
424
425 if let Some(sym) = format.prev_symbol() {
426 self.word_space(sym);
427 } else if is_single_without_cmnts && format.with_space {
428 self.nbsp();
429 } else if !skip_first_break && !format.is_inline() {
430 format.print_break(true, values.len(), &mut self.s);
431 if manual_offset {
432 self.s.offset(self.ind);
433 }
434 }
435
436 if format.is_compact() && !(format.breaks_with_comments() && has_comments) {
437 self.s.cbox(0);
438 }
439
440 let mut last_delimiter_break = !format.with_delimiters;
441 let mut skip_last_break =
442 is_single_without_cmnts || !format.with_delimiters || format.is_inline();
443 for (i, value) in values.iter().enumerate() {
444 let is_last = i == values.len() - 1;
445 if self
446 .print_comments(get_span(value).lo(), CommentConfig::skip_ws().mixed_prev_space())
447 .is_some_and(|cmnt| cmnt.is_mixed())
448 && format.breaks_cmnts
449 {
450 self.hardbreak(); }
452
453 if !(is_last && get_span(value).is_dummy()) {
455 print(self, value);
456 }
457
458 let next_span = if is_last { None } else { Some(get_span(&values[i + 1])) };
459 let next_pos = next_span.map(Span::lo).unwrap_or(pos_hi);
460 let cmnt_before_next =
461 self.peek_comment_before(next_pos).map(|cmnt| (cmnt.span, cmnt.style));
462
463 if !is_last {
464 if cmnt_before_next.is_some_and(|(cmnt_span, _)| {
466 let span = self.cursor.span(cmnt_span.lo());
467 self.inline_config.is_disabled(span)
468 && self.sm.span_to_snippet(span).is_ok_and(|snip| !snip.contains(','))
470 }) {
471 self.print_comments(
472 next_pos,
473 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(),
474 );
475 }
476 self.print_word(",");
477 }
478
479 if !is_last
480 && format.breaks_cmnts
481 && cmnt_before_next.is_some_and(|(cmnt_span, cmnt_style)| {
482 let disabled = self.inline_config.is_disabled(cmnt_span);
483 (cmnt_style.is_mixed() && !disabled) || (cmnt_style.is_isolated() && disabled)
484 })
485 {
486 self.hardbreak(); }
488
489 let comment_config = if !is_last || format.with_delimiters {
491 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space()
492 } else {
493 CommentConfig::skip_ws().no_breaks().mixed_prev_space()
494 };
495 let with_trailing = self.print_comments(next_pos, comment_config).is_some();
496
497 if is_last && with_trailing {
498 if self.is_bol_or_only_ind() {
499 self.break_offset_if_not_bol(0, -self.ind, false);
502 } else {
503 self.s.break_offset(SIZE_INFINITY as usize, -self.ind);
504 }
505 skip_last_break = true;
506 last_delimiter_break = false;
507 }
508
509 if let Some(next_span) = next_span
511 && !self.is_bol_or_only_ind()
512 && !self.inline_config.is_disabled(next_span)
513 && !next_span.is_dummy()
514 {
515 format.print_break(false, values.len(), &mut self.s);
516 if manual_offset {
517 self.s.offset(self.ind);
518 }
519 }
520 }
521
522 if format.is_compact() && !(format.breaks_with_comments() && has_comments) {
523 self.end();
524 }
525 if !skip_last_break {
526 if let Some(sym) = format.post_symbol() {
527 format.print_break(false, values.len(), &mut self.s);
528 self.s.offset(-self.ind);
529 self.word(sym);
530 } else {
531 format.print_break(true, values.len(), &mut self.s);
532 self.s.offset(-self.ind);
533 }
534 } else if is_single_without_cmnts && format.with_space {
535 self.nbsp();
536 } else if let Some(sym) = format.post_symbol() {
537 self.nbsp();
538 self.word(sym);
539 }
540
541 if !manual_offset {
542 self.end();
543 }
544 self.cursor.advance_to(pos_hi, true);
545
546 if last_delimiter_break {
547 format.print_break(true, values.len(), &mut self.s);
548 }
549 }
550
551 pub(super) fn print_path(&mut self, path: &'ast ast::PathSlice, consistent_break: bool) {
552 if consistent_break {
553 self.s.cbox(self.ind);
554 } else {
555 self.s.ibox(self.ind);
556 }
557 for (pos, ident) in path.segments().iter().delimited() {
558 self.print_ident(ident);
559 if !pos.is_last {
560 if !self.emit_or_revert {
561 self.zerobreak();
562 }
563 self.word(".");
564 }
565 }
566 self.end();
567 }
568
569 pub(super) fn print_block_inner<T: Debug>(
570 &mut self,
571 block: &'ast [T],
572 block_format: BlockFormat,
573 mut print: impl FnMut(&mut Self, &'ast T),
574 mut get_block_span: impl FnMut(&'ast T) -> Span,
575 pos_hi: BytePos,
576 ) {
577 if block_format.attempt_single_line() && block.len() == 1 {
579 self.print_single_line_block(block, block_format, print, get_block_span);
580 return;
581 }
582
583 if block.is_empty() {
585 self.print_empty_block(block_format, pos_hi);
586 return;
587 }
588
589 self.block_depth += 1;
591
592 let block_lo = get_block_span(&block[0]).lo();
594 match block_format {
595 BlockFormat::NoBraces(None) => {
596 if !self.handle_span(self.cursor.span(block_lo), false) {
597 self.print_comments(block_lo, CommentConfig::default());
598 }
599 self.s.cbox(0);
600 }
601 BlockFormat::NoBraces(Some(offset)) => {
602 let enabled =
603 !self.inline_config.is_disabled(Span::new(block_lo, block_lo + BytePos(1)))
604 && !self.handle_span(self.cursor.span(block_lo), true);
605 match self
606 .peek_comment()
607 .and_then(|cmnt| (cmnt.span.hi() < block_lo).then_some((cmnt.span, cmnt.style)))
608 {
609 Some((span, style)) => {
610 if enabled {
611 if !self.inline_config.is_disabled(span) || style.is_isolated() {
613 self.cursor.advance_to(span.lo(), true);
614 self.break_offset(SIZE_INFINITY as usize, offset);
615 }
616 if let Some(cmnt) = self.print_comments(
617 block_lo,
618 CommentConfig::skip_leading_ws(false).offset(offset),
619 ) && !cmnt.is_mixed()
620 && !cmnt.is_blank()
621 {
622 self.s.offset(offset);
623 }
624 } else if style.is_isolated() {
625 self.print_sep_unhandled(Separator::Hardbreak);
626 self.s.offset(offset);
627 }
628 }
629 None => {
630 if enabled {
631 self.zerobreak();
632 self.s.offset(offset);
633 } else if self.cursor.enabled {
634 self.print_sep_unhandled(Separator::Space);
635 self.s.offset(offset);
636 self.cursor.advance_to(block_lo, true);
637 }
638 }
639 }
640 self.s.cbox(self.ind);
641 }
642 _ => {
643 self.print_word("{");
644 self.s.cbox(self.ind);
645 if !self.handle_span(self.cursor.span(block_lo), false)
646 && self
647 .print_comments(block_lo, CommentConfig::default())
648 .is_none_or(|cmnt| cmnt.is_mixed())
649 {
650 self.hardbreak_if_nonempty();
651 }
652 }
653 }
654
655 for (i, stmt) in block.iter().enumerate() {
657 let is_last = i == block.len() - 1;
658 print(self, stmt);
659
660 let is_disabled = self.inline_config.is_disabled(get_block_span(stmt));
661 let (next_enabled, next_lo) = if is_last {
662 (false, None)
663 } else {
664 let next_span = get_block_span(&block[i + 1]);
665 (
666 !self.inline_config.is_disabled(next_span),
667 self.peek_comment_before(next_span.lo()).is_none().then_some(next_span.lo()),
668 )
669 };
670
671 if !is_disabled
673 && next_enabled
674 && (!is_last
675 || self.peek_comment_before(pos_hi).is_some_and(|cmnt| cmnt.style.is_mixed()))
676 {
677 self.hardbreak_if_not_bol();
678 continue;
679 }
680 if is_disabled
683 && next_enabled
684 && let Some(next_lo) = next_lo
685 && self
686 .peek_comment_before(next_lo)
687 .is_none_or(|cmnt| self.inline_config.is_disabled(cmnt.span))
688 {
689 self.hardbreak_if_not_bol()
690 }
691 }
692
693 self.print_comments(
694 pos_hi,
695 CommentConfig::skip_trailing_ws().mixed_no_break().mixed_prev_space(),
696 );
697 if !block_format.breaks() {
698 if !self.last_token_is_break() {
699 self.hardbreak();
700 }
701 self.s.offset(-self.ind);
702 }
703 self.end();
704 if block_format.with_braces() {
705 self.print_word("}");
706 }
707
708 self.block_depth -= 1;
710 }
711
712 fn print_single_line_block<T: Debug>(
713 &mut self,
714 block: &'ast [T],
715 block_format: BlockFormat,
716 mut print: impl FnMut(&mut Self, &'ast T),
717 mut get_block_span: impl FnMut(&'ast T) -> Span,
718 ) {
719 self.s.cbox(self.ind);
720
721 match block_format {
722 BlockFormat::Compact(true) => {
723 self.scan_break(BreakToken { pre_break: Some("{"), ..Default::default() });
724 print(self, &block[0]);
725 self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default());
726 self.s.scan_break(BreakToken { post_break: Some("}"), ..Default::default() });
727 self.s.offset(-self.ind);
728 }
729 _ => {
730 self.word("{");
731 self.space();
732 print(self, &block[0]);
733 self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default());
734 self.space_if_not_bol();
735 self.s.offset(-self.ind);
736 self.word("}");
737 }
738 }
739
740 self.end();
741 }
742
743 fn print_empty_block(&mut self, block_format: BlockFormat, pos_hi: BytePos) {
744 let has_braces = block_format.with_braces();
745
746 if self.peek_comment_before(pos_hi).is_none_or(|c| c.style.is_trailing()) {
748 if self.config.bracket_spacing {
749 if has_braces {
750 self.word("{ }");
751 } else {
752 self.nbsp();
753 }
754 } else if has_braces {
755 self.word("{}");
756 }
757 self.print_comments(pos_hi, CommentConfig::skip_ws());
758 return;
759 }
760
761 if has_braces {
763 self.word("{");
764 }
765 let offset = if let BlockFormat::NoBraces(Some(off)) = block_format { off } else { 0 };
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) const 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) const 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) const fn is_consistent(&self) -> bool {
836 matches!(self.kind, ListFormatKind::Consistent)
837 }
838
839 pub(crate) const fn is_compact(&self) -> bool {
840 matches!(self.kind, ListFormatKind::Compact)
841 }
842
843 pub(crate) const fn is_inline(&self) -> bool {
844 matches!(self.kind, ListFormatKind::Inline)
845 }
846
847 pub(crate) const 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) const 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) const 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) const 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) const 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) const 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) const fn with_braces(&self) -> bool {
951 !matches!(self, Self::NoBraces(_))
952 }
953 pub(crate) const fn breaks(&self) -> bool {
954 matches!(self, Self::NoBraces(None))
955 }
956
957 pub(crate) const fn attempt_single_line(&self) -> bool {
958 matches!(self, Self::Compact(_))
959 }
960}