diff --git a/crates/rome_formatter/src/format_element.rs b/crates/rome_formatter/src/format_element.rs index 4d51bb64716..a4b26939326 100644 --- a/crates/rome_formatter/src/format_element.rs +++ b/crates/rome_formatter/src/format_element.rs @@ -241,6 +241,29 @@ pub fn line_suffix(element: impl Into) -> FormatElement { FormatElement::LineSuffix(Box::new(element.into())) } +/// Inserts a boundary for line suffixes that forces to print all pending line suffixes. Helpful +/// if a line sufix shouldn't pass a certain point. +/// +/// ## Examples +/// +/// Forces the line suffix "c" to be printed before the token `d`. +/// ``` +/// use rome_formatter::Formatted; +/// use rome_formatter::prelude::*; +/// +/// let elements = format_elements![token("a"), line_suffix(token("c")), token("b"), line_suffix_boundary(), token("d")]; +/// +/// assert_eq!( +/// "abc\nd", +/// Formatted::new(elements, PrinterOptions::default()) +/// .print() +/// .as_code() +/// ); +/// ``` +pub const fn line_suffix_boundary() -> FormatElement { + FormatElement::LineSuffixBoundary +} + /// Mark a [FormatElement] as being a piece of trivia /// /// This does not directly influence how this content will be printed, but some @@ -712,6 +735,41 @@ pub fn group_elements_with_options( format_elements![leading, group, trailing] } +/// IR element that forces the parent group to print in expanded mode. +/// +/// Has no effect if used outside of a group or element that introduce implicit groups (fill element). +/// +/// ## Examples +/// +/// ``` +/// use rome_formatter::{Formatted, LineWidth}; +/// use rome_formatter::prelude::*; +/// +/// let elements = group_elements(format_elements![ +/// token("["), +/// soft_block_indent(format_elements![ +/// token("'Good morning! How are you today?',"), +/// soft_line_break_or_space(), +/// token("2,"), +/// expand_parent(), // Forces the parent to expand +/// soft_line_break_or_space(), +/// token("3"), +/// ]), +/// token("]"), +/// ]); +/// +/// assert_eq!( +/// "[\n\t'Good morning! How are you today?',\n\t2,\n\t3\n]", +/// Formatted::new(elements, PrinterOptions::default()).print().as_code() +/// ); +/// ``` +/// +/// ## Prettier +/// Equivalent to Prettier's `break_parent` IR element +pub const fn expand_parent() -> FormatElement { + FormatElement::ExpandParent +} + /// Creates a group that forces all elements inside it to be printed on a /// single line. This behavior can in turn be escaped by introducing an inner /// `Group` element that will resume the normal breaking behavior of the printer. @@ -1060,6 +1118,9 @@ pub enum FormatElement { /// See [crate::group_elements] for documentation and examples. Group(Group), + /// Forces the parent group to print in expanded mode. + ExpandParent, + /// See [crate::hard_group_elements] for documentation and examples. HardGroup(Group), @@ -1079,6 +1140,10 @@ pub enum FormatElement { /// Delay the printing of its content until the next line break LineSuffix(Content), + /// Prevents that line suffixes move past this boundary. Forces the printer to print any pending + /// line suffixes, potentially by inserting a hard line break. + LineSuffixBoundary, + /// Special semantic element letting the printer and formatter know this is /// a trivia content, and it should only have a limited influence on the /// formatting (for instance line breaks contained within will not cause @@ -1163,11 +1228,13 @@ impl Debug for FormatElement { FormatElement::LineSuffix(content) => { fmt.debug_tuple("LineSuffix").field(content).finish() } + FormatElement::LineSuffixBoundary => write!(fmt, "LineSuffixBoundary"), FormatElement::Comment(content) => fmt.debug_tuple("Comment").field(content).finish(), FormatElement::Verbatim(verbatim) => fmt .debug_tuple("Verbatim") .field(&verbatim.element) .finish(), + FormatElement::ExpandParent => write!(fmt, "ExpandParent"), } } } @@ -1556,6 +1623,8 @@ impl FormatElement { FormatElement::LineSuffix(_) => false, FormatElement::Comment(content) => content.will_break(), FormatElement::Verbatim(verbatim) => verbatim.element.will_break(), + FormatElement::LineSuffixBoundary => false, + FormatElement::ExpandParent => true, } } diff --git a/crates/rome_formatter/src/printer/mod.rs b/crates/rome_formatter/src/printer/mod.rs index e1927dd3f30..3fa16144e14 100644 --- a/crates/rome_formatter/src/printer/mod.rs +++ b/crates/rome_formatter/src/printer/mod.rs @@ -6,7 +6,7 @@ use crate::format_element::{ ConditionalGroupContent, Group, LineMode, List, PrintMode, VerbatimKind, }; use crate::intersperse::Intersperse; -use crate::{FormatElement, GroupId, Printed, SourceMarker, TextRange}; +use crate::{hard_line_break, FormatElement, GroupId, Printed, SourceMarker, TextRange}; use crate::prelude::Line; use rome_rowan::TextSize; @@ -221,6 +221,11 @@ impl<'a> Printer<'a> { .line_suffixes .push(PrintElementCall::new(&**suffix, args)); } + FormatElement::LineSuffixBoundary => { + const HARD_BREAK: &FormatElement = &hard_line_break(); + self.queue_line_suffixes(HARD_BREAK, args, queue); + } + FormatElement::Comment(content) => { queue.enqueue(PrintElementCall::new(content.as_ref(), args)); } @@ -235,6 +240,13 @@ impl<'a> Printer<'a> { queue.enqueue(PrintElementCall::new(&verbatim.element, args)); } + FormatElement::ExpandParent => { + // No-op, only has an effect on `fits` + debug_assert!( + !args.mode.is_flat(), + "Fits should always return false for `ExpandParent`" + ); + } } } @@ -597,6 +609,7 @@ fn fits_on_line<'a>( pending_indent: printer.state.pending_indent, pending_space: printer.state.pending_space, line_width: printer.state.line_width, + has_line_suffix: !printer.state.line_suffixes.is_empty(), }; let result = loop { @@ -738,9 +751,11 @@ fn fits_element_on_line<'a, 'rest>( } FormatElement::LineSuffix(_) => { - // The current behavior is to return `false` for all line suffixes if trying to print - // something in "flat" mode. - if args.mode.is_flat() { + state.has_line_suffix = true; + } + + FormatElement::LineSuffixBoundary => { + if state.has_line_suffix { return Fits::No; } } @@ -750,6 +765,11 @@ fn fits_element_on_line<'a, 'rest>( FormatElement::Verbatim(verbatim) => { queue.enqueue(PrintElementCall::new(&verbatim.element, args)) } + FormatElement::ExpandParent => { + if args.mode.is_flat() || args.hard_group { + return Fits::No; + } + } } Fits::Maybe @@ -779,6 +799,7 @@ impl From for Fits { struct MeasureState { pending_indent: u16, pending_space: bool, + has_line_suffix: bool, line_width: usize, } diff --git a/crates/rome_js_formatter/src/formatter.rs b/crates/rome_js_formatter/src/formatter.rs index ad02260f4af..8b3a89fba66 100644 --- a/crates/rome_js_formatter/src/formatter.rs +++ b/crates/rome_js_formatter/src/formatter.rs @@ -216,7 +216,10 @@ where ]), ] } else { - line_suffix(format_elements![space_token(), comment, space_token()]) + format_elements![ + line_suffix(format_elements![space_token(), comment]), + expand_parent() + ] }; elements.push(crate::comment(content)); diff --git a/crates/rome_js_formatter/src/js/expressions/assignment_expression.rs b/crates/rome_js_formatter/src/js/expressions/assignment_expression.rs index d000b469c52..68776cf3a66 100644 --- a/crates/rome_js_formatter/src/js/expressions/assignment_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/assignment_expression.rs @@ -21,6 +21,7 @@ impl FormatNodeFields for FormatNodeRule for FormatNodeRule {}; -f4 = () => {}; // Comment +f4 = // Comment +() => {}; f5 = // Comment () => {}; -f6 = - /* comment */ +f6 = /* comment */ // Comment () => {}; diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/assignment-comments/number.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/assignment-comments/number.js.snap index 85fe4940954..52e3589557c 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/assignment-comments/number.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/assignment-comments/number.js.snap @@ -1,8 +1,6 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs -assertion_line: 144 expression: number.js - --- # Input ```js @@ -70,8 +68,7 @@ fnNumber = fnNumber = /* comment */ 3; -fnNumber = - /* comments0 */ +fnNumber = /* comments0 */ /* comments1 */ 3; diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/assignment-comments/string.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/assignment-comments/string.js.snap index fd32295876c..1b2ca0d8749 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/assignment-comments/string.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/assignment-comments/string.js.snap @@ -1,8 +1,6 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs -assertion_line: 144 expression: string.js - --- # Input ```js @@ -118,12 +116,12 @@ fnString = "long" + "string"; -fnString = - // Comment0 +fnString = // Comment0 // Comment1 "some" + "long" + "string"; -fnString = "some" + "long" + "string"; // Comment +fnString = // Comment +"some" + "long" + "string"; fnString = // Comment