diff --git a/crates/oxc_prettier/src/doc.rs b/crates/oxc_prettier/src/doc.rs index 987f14849b63d..155c22e791e16 100644 --- a/crates/oxc_prettier/src/doc.rs +++ b/crates/oxc_prettier/src/doc.rs @@ -77,12 +77,19 @@ impl Line { pub struct Group<'a> { pub contents: Vec<'a, Doc<'a>>, pub should_break: bool, + pub expanded_states: Option>>, pub id: Option, } impl<'a> Group<'a> { pub fn new(contents: Vec<'a, Doc<'a>>, should_break: bool) -> Self { - Self { contents, should_break, id: None } + Self { contents, should_break, id: None, expanded_states: None } + } + pub fn new_conditional_group( + contents: Vec<'a, Doc<'a>>, + expanded_states: Vec<'a, Doc<'a>>, + ) -> Self { + Self { contents, should_break: false, id: None, expanded_states: Some(expanded_states) } } pub fn with_id(mut self, id: GroupId) -> Self { self.id = Some(id); @@ -244,6 +251,10 @@ fn print_doc_to_debug(doc: &Doc<'_>) -> std::string::String { string.push_str("])"); } Doc::Group(group) => { + if group.expanded_states.is_some() { + string.push_str("conditionalGroup([\n"); + } + string.push_str("group([\n"); for (idx, doc) in group.contents.iter().enumerate() { string.push_str(&print_doc_to_debug(doc)); @@ -257,6 +268,17 @@ fn print_doc_to_debug(doc: &Doc<'_>) -> std::string::String { string.push_str(&format!(", id: {id}")); } string.push_str(" })"); + + if let Some(expanded_states) = &group.expanded_states { + string.push_str(",\n"); + for (idx, doc) in expanded_states.iter().enumerate() { + string.push_str(&print_doc_to_debug(doc)); + if idx != expanded_states.len() - 1 { + string.push_str(", "); + } + } + string.push(']'); + } } Doc::Line(Line { soft, hard, .. }) => { if *soft { diff --git a/crates/oxc_prettier/src/macros.rs b/crates/oxc_prettier/src/macros.rs index a6219495e3a6a..3196ecc7c472d 100644 --- a/crates/oxc_prettier/src/macros.rs +++ b/crates/oxc_prettier/src/macros.rs @@ -98,6 +98,35 @@ macro_rules! group { }; } +#[macro_export] +macro_rules! conditional_group { + ($p:ident, $c: expr, $( $x:expr ),* $(,)?) => { + { + use $crate::doc::DocBuilder; + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + let contents = $p.vec_single($c); + Doc::Group($crate::doc::Group::new_conditional_group(contents, temp_vec)) + } + }; +} + +#[macro_export] +macro_rules! group_break { + ($p:ident, $( $x:expr ),* $(,)?) => { + { + use $crate::doc::DocBuilder; + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + Doc::Group($crate::doc::Group::new(temp_vec, true)) + } + }; +} + #[macro_export] macro_rules! if_break { ($p:ident, $s:expr, $flat:expr, $group_id:expr) => {{ diff --git a/crates/oxc_prettier/src/printer/mod.rs b/crates/oxc_prettier/src/printer/mod.rs index 231e301de2ea6..66024e26d42c8 100644 --- a/crates/oxc_prettier/src/printer/mod.rs +++ b/crates/oxc_prettier/src/printer/mod.rs @@ -140,13 +140,28 @@ impl<'a> Printer<'a> { self.cmds.push(Command::new(indent, Mode::Flat, cmd.doc)); } else { let Doc::Group(group) = cmd.doc else { unreachable!() }; - self.cmds.extend( - group - .contents - .into_iter() - .rev() - .map(|doc| Command::new(indent, Mode::Break, doc)), - ); + + if let Some(mut expanded_states) = group.expanded_states { + let most_expanded = expanded_states.pop().unwrap(); + if group.should_break { + self.cmds.push(Command::new(indent, Mode::Break, most_expanded)); + return; + } + for state in expanded_states { + let cmd = Command::new(indent, Mode::Flat, state); + if self.fits(&cmd, remaining_width) { + self.cmds.push(cmd); + return; + } + } + self.cmds.push(Command::new(indent, Mode::Break, most_expanded)); + } else { + self.cmds.push(Command::new( + indent, + Mode::Break, + Doc::Array(group.contents), + )); + } } self.set_group_mode_from_last_cmd(group_id); } @@ -360,9 +375,16 @@ impl<'a> Printer<'a> { } Doc::Group(group) => { let mode = if group.should_break { Mode::Break } else { mode }; - for d in group.contents.iter().rev() { - queue.push_front((mode, d)); - } + if group.expanded_states.is_some() && mode.is_break() { + queue.push_front(( + mode, + group.expanded_states.as_ref().unwrap().last().unwrap(), + )); + } else { + for d in group.contents.iter().rev() { + queue.push_front((mode, d)); + } + }; } Doc::IfBreak(if_break_doc) => { let group_mode = if_break_doc @@ -413,11 +435,20 @@ impl<'a> Printer<'a> { /// Reference: /// * https://github.com/prettier/prettier/blob/main/src/document/utils.js#L156-L185 pub fn propagate_breaks(doc: &mut Doc<'_>) -> bool { + let check_array = |arr: &mut oxc_allocator::Vec<'_, Doc<'_>>| { + arr.iter_mut().rev().any(|doc| Self::propagate_breaks(doc)) + }; + match doc { Doc::BreakParent => true, Doc::Group(group) => { - let should_break = - group.contents.iter_mut().rev().any(|doc| Self::propagate_breaks(doc)); + let mut should_break = false; + if let Some(expanded_states) = &mut group.expanded_states { + should_break = expanded_states.iter_mut().rev().any(Self::propagate_breaks); + } + if !should_break { + should_break = check_array(&mut group.contents); + } if should_break { group.should_break = should_break; } @@ -426,9 +457,7 @@ impl<'a> Printer<'a> { Doc::IfBreak(d) => Self::propagate_breaks(&mut d.break_contents), Doc::Array(arr) | Doc::Indent(arr) - | Doc::IndentIfBreak(IndentIfBreak { contents: arr, .. }) => { - arr.iter_mut().any(|doc| Self::propagate_breaks(doc)) - } + | Doc::IndentIfBreak(IndentIfBreak { contents: arr, .. }) => check_array(arr), _ => false, } }