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