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 if Cursor::new(&s).exactly_one().is_err() {
180 let other_quote = if quote == '\"' { '\'' } else { '\"' };
181 {
182 let s = unsafe { s.as_bytes_mut() };
183 s[prefix.len()] = other_quote as u8;
184 s[s.len() - 1] = other_quote as u8;
185 }
186 debug_assert!(Cursor::new(&s).exactly_one().map(|_| true).unwrap());
187 }
188
189 s
190 }
191
192 pub(super) fn print_tuple_empty(&mut self, pos_lo: BytePos, pos_hi: BytePos) {
193 if self.handle_span(Span::new(pos_lo, pos_hi), true) {
194 return;
195 }
196
197 self.print_inside_parens(|state| {
198 state.s.cbox(state.ind);
199 if let Some(cmnt) =
200 state.print_comments(pos_hi, CommentConfig::skip_ws().mixed_prev_space())
201 {
202 if cmnt.is_mixed() {
203 state.s.offset(-state.ind);
204 } else {
205 state.break_offset_if_not_bol(0, -state.ind, false);
206 }
207 }
208 state.end();
209 });
210 }
211
212 pub(super) fn print_tuple<'a, T, P, S>(
213 &mut self,
214 values: &'a [T],
215 pos_lo: BytePos,
216 pos_hi: BytePos,
217 mut print: P,
218 mut get_span: S,
219 format: ListFormat,
220 ) where
221 P: FnMut(&mut Self, &'a T),
222 S: FnMut(&T) -> Span,
223 {
224 if self.handle_span(Span::new(pos_lo, pos_hi), true) {
225 return;
226 }
227
228 if values.is_empty() {
229 self.print_tuple_empty(pos_lo, pos_hi);
230 return;
231 }
232
233 if !(values.len() == 1 && format.is_inline()) {
234 self.print_inside_parens(|state| {
236 state.commasep(values, pos_lo, pos_hi, print, get_span, format)
237 });
238 return;
239 }
240
241 self.print_inside_parens(|state| {
243 let span = get_span(&values[0]);
244 state.s.cbox(state.ind);
245 let mut skip_break = true;
246 if state.peek_comment_before(span.hi()).is_some() {
247 state.hardbreak();
248 skip_break = false;
249 }
250
251 state.print_comments(span.lo(), CommentConfig::skip_ws().mixed_prev_space());
252 print(state, &values[0]);
253
254 if !state.print_trailing_comment(span.hi(), None) && skip_break {
255 state.neverbreak();
256 } else {
257 state.break_offset_if_not_bol(0, -state.ind, false);
258 }
259 state.end();
260 });
261 }
262
263 pub(super) fn print_array<'a, T, P, S>(
264 &mut self,
265 values: &'a [T],
266 span: Span,
267 print: P,
268 get_span: S,
269 ) where
270 P: FnMut(&mut Self, &'a T),
271 S: FnMut(&T) -> Span,
272 {
273 if self.handle_span(span, false) {
274 return;
275 }
276
277 self.print_word("[");
278 self.commasep(values, span.lo(), span.hi(), print, get_span, ListFormat::compact());
279 self.print_word("]");
280 }
281
282 pub(super) fn commasep_opening_logic<T, S>(
283 &mut self,
284 values: &[T],
285 mut get_span: S,
286 format: ListFormat,
287 manual_opening: bool,
288 ) -> bool
289 where
290 S: FnMut(&T) -> Span,
291 {
292 let Some(span) = values.first().map(&mut get_span) else {
293 return false;
294 };
295
296 if span.is_dummy()
299 && let Some(next_pos) = values.get(1).map(|v| get_span(v).lo())
300 && self.peek_comment_before(next_pos).is_some()
301 {
302 return true;
303 }
304
305 if let Some((cmnt_span, cmnt_style)) =
307 self.peek_comment_before(span.lo()).map(|c| (c.span, c.style))
308 {
309 let cmnt_disabled = self.inline_config.is_disabled(cmnt_span);
310 if self.cursor.enabled && cmnt_disabled && cmnt_style.is_isolated() {
312 self.print_sep(Separator::Hardbreak);
313 if !format.with_delimiters {
314 self.s.offset(self.ind);
315 }
316 };
317
318 if manual_opening {
321 self.hardbreak();
322 self.s.offset(self.ind);
323 return true;
324 }
325
326 let cmnt_config = if format.with_delimiters {
327 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space()
328 } else {
329 CommentConfig::skip_ws().no_breaks().mixed_prev_space().offset(self.ind)
330 };
331 if let Some(last_style) = self.print_comments(span.lo(), cmnt_config) {
333 match (cmnt_style.is_mixed(), last_style.is_mixed()) {
334 (true, true) => {
335 if format.breaks_cmnts {
336 self.hardbreak();
337 } else {
338 self.space();
339 }
340 if !format.with_delimiters && !cmnt_disabled {
341 self.s.offset(self.ind);
342 }
343 }
344 (false, true) => {
345 self.nbsp();
346 }
347 (false, false) if !format.with_delimiters && !cmnt_disabled => {
348 self.hardbreak();
349 self.s.offset(self.ind);
350 }
351 _ => {}
352 }
353 }
354 if self.cursor.enabled {
355 self.cursor.advance_to(span.lo(), true);
356 }
357 return true;
358 }
359
360 if self.cursor.enabled {
361 self.cursor.advance_to(span.lo(), true);
362 }
363
364 if !values.is_empty() && !format.with_delimiters {
365 format.print_break(true, values.len(), &mut self.s);
366 self.s.offset(self.ind);
367 return true;
368 }
369
370 false
371 }
372
373 pub(super) fn commasep<'a, T, P, S>(
374 &mut self,
375 values: &'a [T],
376 _pos_lo: BytePos,
377 pos_hi: BytePos,
378 mut print: P,
379 mut get_span: S,
380 format: ListFormat,
381 ) where
382 P: FnMut(&mut Self, &'a T),
383 S: FnMut(&T) -> Span,
384 {
385 if values.is_empty() {
386 return;
387 }
388
389 let has_comments =
392 self.peek_comment_before(get_span(&values[0]).lo()).is_some() ||
394 values.windows(2).any(|w| self.peek_comment_between(get_span(&w[0]).hi(), get_span(&w[1]).lo()).is_some()) ||
396 self.peek_comment_between(get_span(values.last().unwrap()).hi(), pos_hi).is_some();
398
399 let manual_opening =
403 format.is_consistent() && !format.with_delimiters && self.call_with_opts_and_args;
404 let manual_offset = !has_comments && manual_opening;
406
407 let is_single_without_cmnts = values.len() == 1 && !format.break_single && !has_comments;
408 let skip_first_break = if format.with_delimiters || format.is_inline() {
409 self.s.cbox(if format.no_ind { 0 } else { self.ind });
410 if is_single_without_cmnts {
411 true
412 } else {
413 self.commasep_opening_logic(values, &mut get_span, format, manual_opening)
414 }
415 } else {
416 let res = self.commasep_opening_logic(values, &mut get_span, format, manual_opening);
417 if !manual_offset {
418 self.s.cbox(if format.no_ind { 0 } else { self.ind });
419 }
420 res
421 };
422
423 if let Some(sym) = format.prev_symbol() {
424 self.word_space(sym);
425 } else if is_single_without_cmnts && format.with_space {
426 self.nbsp();
427 } else if !skip_first_break && !format.is_inline() {
428 format.print_break(true, values.len(), &mut self.s);
429 if manual_offset {
430 self.s.offset(self.ind);
431 }
432 }
433
434 if format.is_compact() && !(format.breaks_with_comments() && has_comments) {
435 self.s.cbox(0);
436 }
437
438 let mut last_delimiter_break = !format.with_delimiters;
439 let mut skip_last_break =
440 is_single_without_cmnts || !format.with_delimiters || format.is_inline();
441 for (i, value) in values.iter().enumerate() {
442 let is_last = i == values.len() - 1;
443 if self
444 .print_comments(get_span(value).lo(), CommentConfig::skip_ws().mixed_prev_space())
445 .is_some_and(|cmnt| cmnt.is_mixed())
446 && format.breaks_cmnts
447 {
448 self.hardbreak(); }
450
451 if !(is_last && get_span(value).is_dummy()) {
453 print(self, value);
454 }
455
456 let next_span = if is_last { None } else { Some(get_span(&values[i + 1])) };
457 let next_pos = next_span.map(Span::lo).unwrap_or(pos_hi);
458 let cmnt_before_next =
459 self.peek_comment_before(next_pos).map(|cmnt| (cmnt.span, cmnt.style));
460
461 if !is_last {
462 if cmnt_before_next.is_some_and(|(cmnt_span, _)| {
464 let span = self.cursor.span(cmnt_span.lo());
465 self.inline_config.is_disabled(span)
466 && self.sm.span_to_snippet(span).is_ok_and(|snip| !snip.contains(','))
468 }) {
469 self.print_comments(
470 next_pos,
471 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(),
472 );
473 }
474 self.print_word(",");
475 }
476
477 if !is_last
478 && format.breaks_cmnts
479 && cmnt_before_next.is_some_and(|(cmnt_span, cmnt_style)| {
480 let disabled = self.inline_config.is_disabled(cmnt_span);
481 (cmnt_style.is_mixed() && !disabled) || (cmnt_style.is_isolated() && disabled)
482 })
483 {
484 self.hardbreak(); }
486
487 let comment_config = if !is_last || format.with_delimiters {
489 CommentConfig::skip_ws().mixed_no_break().mixed_prev_space()
490 } else {
491 CommentConfig::skip_ws().no_breaks().mixed_prev_space()
492 };
493 let with_trailing = self.print_comments(next_pos, comment_config).is_some();
494
495 if is_last && with_trailing {
496 if self.is_bol_or_only_ind() {
497 self.break_offset_if_not_bol(0, -self.ind, false);
500 } else {
501 self.s.break_offset(SIZE_INFINITY as usize, -self.ind);
502 }
503 skip_last_break = true;
504 last_delimiter_break = false;
505 }
506
507 if let Some(next_span) = next_span
509 && !self.is_bol_or_only_ind()
510 && !self.inline_config.is_disabled(next_span)
511 && !next_span.is_dummy()
512 {
513 format.print_break(false, values.len(), &mut self.s);
514 if manual_offset {
515 self.s.offset(self.ind);
516 }
517 }
518 }
519
520 if format.is_compact() && !(format.breaks_with_comments() && has_comments) {
521 self.end();
522 }
523 if !skip_last_break {
524 if let Some(sym) = format.post_symbol() {
525 format.print_break(false, values.len(), &mut self.s);
526 self.s.offset(-self.ind);
527 self.word(sym);
528 } else {
529 format.print_break(true, values.len(), &mut self.s);
530 self.s.offset(-self.ind);
531 }
532 } else if is_single_without_cmnts && format.with_space {
533 self.nbsp();
534 } else if let Some(sym) = format.post_symbol() {
535 self.nbsp();
536 self.word(sym);
537 }
538
539 if !manual_offset {
540 self.end();
541 }
542 self.cursor.advance_to(pos_hi, true);
543
544 if last_delimiter_break {
545 format.print_break(true, values.len(), &mut self.s);
546 }
547 }
548
549 pub(super) fn print_path(&mut self, path: &'ast ast::PathSlice, consistent_break: bool) {
550 if consistent_break {
551 self.s.cbox(self.ind);
552 } else {
553 self.s.ibox(self.ind);
554 }
555 for (pos, ident) in path.segments().iter().delimited() {
556 self.print_ident(ident);
557 if !pos.is_last {
558 if !self.emit_or_revert {
559 self.zerobreak();
560 }
561 self.word(".");
562 }
563 }
564 self.end();
565 }
566
567 pub(super) fn print_block_inner<T: Debug>(
568 &mut self,
569 block: &'ast [T],
570 block_format: BlockFormat,
571 mut print: impl FnMut(&mut Self, &'ast T),
572 mut get_block_span: impl FnMut(&'ast T) -> Span,
573 pos_hi: BytePos,
574 ) {
575 if block_format.attempt_single_line() && block.len() == 1 {
577 self.print_single_line_block(block, block_format, print, get_block_span);
578 return;
579 }
580
581 if block.is_empty() {
583 self.print_empty_block(block_format, pos_hi);
584 return;
585 }
586
587 self.block_depth += 1;
589
590 let block_lo = get_block_span(&block[0]).lo();
592 match block_format {
593 BlockFormat::NoBraces(None) => {
594 if !self.handle_span(self.cursor.span(block_lo), false) {
595 self.print_comments(block_lo, CommentConfig::default());
596 }
597 self.s.cbox(0);
598 }
599 BlockFormat::NoBraces(Some(offset)) => {
600 let enabled =
601 !self.inline_config.is_disabled(Span::new(block_lo, block_lo + BytePos(1)))
602 && !self.handle_span(self.cursor.span(block_lo), true);
603 match self.peek_comment().and_then(|cmnt| {
604 if cmnt.span.hi() < block_lo { Some((cmnt.span, cmnt.style)) } else { None }
605 }) {
606 Some((span, style)) => {
607 if enabled {
608 if !self.inline_config.is_disabled(span) || style.is_isolated() {
610 self.cursor.advance_to(span.lo(), true);
611 self.break_offset(SIZE_INFINITY as usize, offset);
612 }
613 if let Some(cmnt) = self.print_comments(
614 block_lo,
615 CommentConfig::skip_leading_ws(false).offset(offset),
616 ) && !cmnt.is_mixed()
617 && !cmnt.is_blank()
618 {
619 self.s.offset(offset);
620 }
621 } else if style.is_isolated() {
622 self.print_sep_unhandled(Separator::Hardbreak);
623 self.s.offset(offset);
624 }
625 }
626 None => {
627 if enabled {
628 self.zerobreak();
629 self.s.offset(offset);
630 } else if self.cursor.enabled {
631 self.print_sep_unhandled(Separator::Space);
632 self.s.offset(offset);
633 self.cursor.advance_to(block_lo, true);
634 }
635 }
636 }
637 self.s.cbox(self.ind);
638 }
639 _ => {
640 self.print_word("{");
641 self.s.cbox(self.ind);
642 if !self.handle_span(self.cursor.span(block_lo), false)
643 && self
644 .print_comments(block_lo, CommentConfig::default())
645 .is_none_or(|cmnt| cmnt.is_mixed())
646 {
647 self.hardbreak_if_nonempty();
648 }
649 }
650 }
651
652 for (i, stmt) in block.iter().enumerate() {
654 let is_last = i == block.len() - 1;
655 print(self, stmt);
656
657 let is_disabled = self.inline_config.is_disabled(get_block_span(stmt));
658 let mut next_enabled = false;
659 let mut next_lo = None;
660 if !is_last {
661 let next_span = get_block_span(&block[i + 1]);
662 next_enabled = !self.inline_config.is_disabled(next_span);
663 next_lo =
664 self.peek_comment_before(next_span.lo()).is_none().then_some(next_span.lo());
665 }
666
667 if !is_disabled
669 && next_enabled
670 && (!is_last
671 || self.peek_comment_before(pos_hi).is_some_and(|cmnt| cmnt.style.is_mixed()))
672 {
673 self.hardbreak_if_not_bol();
674 continue;
675 }
676 if is_disabled
679 && next_enabled
680 && let Some(next_lo) = next_lo
681 && self
682 .peek_comment_before(next_lo)
683 .is_none_or(|cmnt| self.inline_config.is_disabled(cmnt.span))
684 {
685 self.hardbreak_if_not_bol()
686 }
687 }
688
689 self.print_comments(
690 pos_hi,
691 CommentConfig::skip_trailing_ws().mixed_no_break().mixed_prev_space(),
692 );
693 if !block_format.breaks() {
694 if !self.last_token_is_break() {
695 self.hardbreak();
696 }
697 self.s.offset(-self.ind);
698 }
699 self.end();
700 if block_format.with_braces() {
701 self.print_word("}");
702 }
703
704 self.block_depth -= 1;
706 }
707
708 fn print_single_line_block<T: Debug>(
709 &mut self,
710 block: &'ast [T],
711 block_format: BlockFormat,
712 mut print: impl FnMut(&mut Self, &'ast T),
713 mut get_block_span: impl FnMut(&'ast T) -> Span,
714 ) {
715 self.s.cbox(self.ind);
716
717 match block_format {
718 BlockFormat::Compact(true) => {
719 self.scan_break(BreakToken { pre_break: Some("{"), ..Default::default() });
720 print(self, &block[0]);
721 self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default());
722 self.s.scan_break(BreakToken { post_break: Some("}"), ..Default::default() });
723 self.s.offset(-self.ind);
724 }
725 _ => {
726 self.word("{");
727 self.space();
728 print(self, &block[0]);
729 self.print_comments(get_block_span(&block[0]).hi(), CommentConfig::default());
730 self.space_if_not_bol();
731 self.s.offset(-self.ind);
732 self.word("}");
733 }
734 }
735
736 self.end();
737 }
738
739 fn print_empty_block(&mut self, block_format: BlockFormat, pos_hi: BytePos) {
740 let has_braces = block_format.with_braces();
741
742 if self.peek_comment_before(pos_hi).is_none_or(|c| c.style.is_trailing()) {
744 if self.config.bracket_spacing {
745 if has_braces {
746 self.word("{ }");
747 } else {
748 self.nbsp();
749 }
750 } else if has_braces {
751 self.word("{}");
752 }
753 self.print_comments(pos_hi, CommentConfig::skip_ws());
754 return;
755 }
756
757 if has_braces {
759 self.word("{");
760 }
761 let mut offset = 0;
762 if let BlockFormat::NoBraces(Some(off)) = block_format {
763 offset = off;
764 }
765 self.print_comments(
766 pos_hi,
767 self.cmnt_config().offset(offset).mixed_no_break().mixed_prev_space().mixed_post_nbsp(),
768 );
769 self.print_comments(
770 pos_hi,
771 CommentConfig::default().mixed_no_break().mixed_prev_space().mixed_post_nbsp(),
772 );
773 if has_braces {
774 self.word("}");
775 }
776 }
777}
778
779#[derive(Debug, Clone, Copy, PartialEq, Eq)]
781pub(crate) struct ListFormat {
782 kind: ListFormatKind,
784 no_ind: bool,
786 break_single: bool,
788 breaks_cmnts: bool,
790 with_space: bool,
792 with_delimiters: bool,
794}
795
796#[derive(Debug, Clone, Copy, PartialEq, Eq)]
798pub(crate) enum ListFormatKind {
799 AlwaysBreak,
801 Consistent,
803 Compact,
805 Inline,
807 Yul { sym_prev: Option<&'static str>, sym_post: Option<&'static str> },
809}
810
811impl Default for ListFormat {
812 fn default() -> Self {
813 Self {
814 kind: ListFormatKind::Consistent,
815 no_ind: false,
816 break_single: false,
817 breaks_cmnts: false,
818 with_space: false,
819 with_delimiters: true,
820 }
821 }
822}
823
824impl ListFormat {
825 pub(crate) fn prev_symbol(&self) -> Option<&'static str> {
827 if let ListFormatKind::Yul { sym_prev, .. } = self.kind { sym_prev } else { None }
828 }
829
830 pub(crate) fn post_symbol(&self) -> Option<&'static str> {
831 if let ListFormatKind::Yul { sym_post, .. } = self.kind { sym_post } else { None }
832 }
833
834 pub(crate) fn is_consistent(&self) -> bool {
835 matches!(self.kind, ListFormatKind::Consistent)
836 }
837
838 pub(crate) fn is_compact(&self) -> bool {
839 matches!(self.kind, ListFormatKind::Compact)
840 }
841
842 pub(crate) fn is_inline(&self) -> bool {
843 matches!(self.kind, ListFormatKind::Inline)
844 }
845
846 pub(crate) fn breaks_with_comments(&self) -> bool {
847 self.breaks_cmnts
848 }
849
850 pub(crate) fn inline() -> Self {
852 Self { kind: ListFormatKind::Inline, ..Default::default() }
853 }
854
855 pub(crate) fn consistent() -> Self {
856 Self { kind: ListFormatKind::Consistent, ..Default::default() }
857 }
858
859 pub(crate) fn compact() -> Self {
860 Self { kind: ListFormatKind::Compact, ..Default::default() }
861 }
862
863 pub(crate) fn always_break() -> Self {
864 Self {
865 kind: ListFormatKind::AlwaysBreak,
866 breaks_cmnts: true,
867 break_single: true,
868 with_delimiters: true,
869 ..Default::default()
870 }
871 }
872
873 pub(crate) fn yul(sym_prev: Option<&'static str>, sym_post: Option<&'static str>) -> Self {
874 Self {
875 kind: ListFormatKind::Yul { sym_prev, sym_post },
876 breaks_cmnts: true,
877 with_delimiters: true,
878 ..Default::default()
879 }
880 }
881
882 pub(crate) fn without_ind(mut self, without: bool) -> Self {
883 if !matches!(self.kind, ListFormatKind::Inline) {
884 self.no_ind = without;
885 }
886 self
887 }
888
889 pub(crate) fn break_single(mut self, value: bool) -> Self {
890 if !matches!(self.kind, ListFormatKind::Inline) {
891 self.break_single = value;
892 }
893 self
894 }
895
896 pub(crate) fn break_cmnts(mut self) -> Self {
897 if !matches!(self.kind, ListFormatKind::Inline) {
898 self.breaks_cmnts = true;
899 }
900 self
901 }
902
903 pub(crate) fn with_space(mut self) -> Self {
904 if !matches!(self.kind, ListFormatKind::Inline) {
905 self.with_space = true;
906 }
907 self
908 }
909
910 pub(crate) fn with_delimiters(mut self, with: bool) -> Self {
911 if matches!(self.kind, ListFormatKind::Compact | ListFormatKind::Consistent) {
912 self.with_delimiters = with;
913 }
914 self
915 }
916
917 pub(crate) fn print_break(&self, soft: bool, elems: usize, p: &mut Printer) {
919 match self.kind {
920 ListFormatKind::Inline => p.nbsp(), ListFormatKind::AlwaysBreak if elems > 1 || (self.break_single && elems == 1) => {
922 p.hardbreak()
923 }
924 _ => {
925 if soft && !self.with_space {
926 p.zerobreak();
927 } else {
928 p.space();
929 }
930 }
931 }
932 }
933}
934
935#[derive(Debug, Clone, Copy, PartialEq, Eq)]
937#[expect(dead_code)]
938pub(crate) enum BlockFormat {
939 Regular,
940 Compact(bool),
943 NoBraces(Option<isize>),
946}
947
948impl BlockFormat {
949 pub(crate) fn with_braces(&self) -> bool {
950 !matches!(self, Self::NoBraces(_))
951 }
952 pub(crate) fn breaks(&self) -> bool {
953 matches!(self, Self::NoBraces(None))
954 }
955
956 pub(crate) fn attempt_single_line(&self) -> bool {
957 matches!(self, Self::Compact(_))
958 }
959}