diff --git a/crates/oxc_prettier/src/doc.rs b/crates/oxc_prettier/src/doc.rs index ac83aea56..a6b0ac8ad 100644 --- a/crates/oxc_prettier/src/doc.rs +++ b/crates/oxc_prettier/src/doc.rs @@ -21,7 +21,7 @@ pub enum Doc<'a> { /// Groups are usually nested, and the printer will try to fit everything on one line, /// but if it doesn't fit it will break the outermost group first and try again. /// It will continue breaking groups until everything fits (or there are no more groups to break). - Group(Vec<'a, Doc<'a>>), + Group(Group<'a>), /// Specify a line break. /// If an expression fits on one line, the line break will be replaced with a space. /// Line breaks always indent the next line with the current level of indentation. @@ -36,6 +36,18 @@ pub enum Doc<'a> { IfBreak(Box<'a, Doc<'a>>), } +#[derive(Debug)] +pub struct Group<'a> { + pub contents: Vec<'a, Doc<'a>>, + pub should_break: bool, +} + +impl<'a> Group<'a> { + pub fn new(contents: Vec<'a, Doc<'a>>, should_break: bool) -> Self { + Self { contents, should_break } + } +} + #[derive(Clone, Copy)] #[allow(unused)] pub enum Separator { @@ -129,15 +141,17 @@ fn print_do_to_debug(doc: &Doc<'_>) -> std::string::String { } string.push_str("]) \n"); } - Doc::Group(contents) => { + Doc::Group(group) => { string.push_str("group([\n"); - for (idx, doc) in contents.iter().enumerate() { + for (idx, doc) in group.contents.iter().enumerate() { string.push_str(&print_do_to_debug(doc)); - if idx != contents.len() - 1 { + if idx != group.contents.len() - 1 { string.push_str(", "); } } - string.push_str("])\n"); + string.push_str("], { shouldBreak: "); + string.push_str(&group.should_break.to_string()); + string.push_str(" })"); } Doc::Line => { string.push_str("line"); diff --git a/crates/oxc_prettier/src/format/array.rs b/crates/oxc_prettier/src/format/array.rs index fb6dc5a34..54bd5afec 100644 --- a/crates/oxc_prettier/src/format/array.rs +++ b/crates/oxc_prettier/src/format/array.rs @@ -2,7 +2,10 @@ use oxc_ast::ast::*; use oxc_span::Span; use crate::{ - array, comment::DanglingCommentsPrintOptions, doc::Doc, group, indent, softline, ss, Prettier, + array, + comment::DanglingCommentsPrintOptions, + doc::{Doc, Group}, + group, indent, softline, ss, Prettier, }; use super::Format; @@ -68,7 +71,7 @@ pub(super) fn print_array<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Do indent!(p, softline!(), elements) }; parts.push(group!(p, ss!("["), parts_inner, softline!(), ss!("]"))); - Doc::Group(parts) + Doc::Group(Group::new(parts, false)) } fn print_empty_array_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { diff --git a/crates/oxc_prettier/src/format/call_expression.rs b/crates/oxc_prettier/src/format/call_expression.rs index a1cc9db34..ed1799e90 100644 --- a/crates/oxc_prettier/src/format/call_expression.rs +++ b/crates/oxc_prettier/src/format/call_expression.rs @@ -1,7 +1,10 @@ use oxc_allocator::{Box, Vec}; use oxc_ast::ast::*; -use crate::{doc::Doc, if_break, ss, Format, Prettier}; +use crate::{ + doc::{Doc, Group}, + if_break, ss, Format, Prettier, +}; pub(super) fn print_call_expression<'a>( p: &mut Prettier<'a>, @@ -42,5 +45,5 @@ fn print_call_expression_arguments<'a>( parts.push(if_break!(p, ",")); parts.push(Doc::Softline); parts.push(ss!(")")); - Doc::Group(parts) + Doc::Group(Group::new(parts, false)) } diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index b5106f81a..8b0cb526f 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -29,7 +29,7 @@ use oxc_span::GetSpan; use crate::{ array, - doc::{Doc, Separator}, + doc::{Doc, Group, Separator}, format, group, hardline, indent, line, softline, ss, string, wrap, Prettier, }; @@ -229,7 +229,7 @@ impl<'a> Format<'a> for ForInStatement<'a> { parts.push(ss!(")")); let body = format!(p, self.body); parts.push(misc::adjust_clause(p, &self.body, body, false)); - Doc::Group(parts) + Doc::Group(Group::new(parts, false)) }) } } @@ -287,7 +287,7 @@ impl<'a> Format<'a> for ForOfStatement<'a> { parts.push(ss!(")")); let body = format!(p, self.body); parts.push(misc::adjust_clause(p, &self.body, body, false)); - Doc::Group(parts) + Doc::Group(Group::new(parts, false)) }) } } @@ -314,7 +314,7 @@ impl<'a> Format<'a> for WhileStatement<'a> { let body = format!(p, self.body); parts.push(misc::adjust_clause(p, &self.body, body, false)); - Doc::Group(parts) + Doc::Group(Group::new(parts, false)) }) } } @@ -573,7 +573,7 @@ impl<'a> Format<'a> for VariableDeclaration<'a> { parts.push(ss!(";")); } - Doc::Group(parts) + Doc::Group(Group::new(parts, false)) }) } } @@ -1408,7 +1408,7 @@ impl<'a> Format<'a> for ObjectProperty<'a> { parts.push(ss!(": ")); parts.push(format!(p, self.value)); } - Doc::Group(parts) + Doc::Group(Group::new(parts, false)) } } } @@ -1660,7 +1660,7 @@ impl<'a> Format<'a> for ImportExpression<'a> { } parts.push(ss!(")")); - Doc::Group(parts) + Doc::Group(Group::new(parts, false)) } } diff --git a/crates/oxc_prettier/src/format/object.rs b/crates/oxc_prettier/src/format/object.rs index 74476fcb1..972961c12 100644 --- a/crates/oxc_prettier/src/format/object.rs +++ b/crates/oxc_prettier/src/format/object.rs @@ -1,6 +1,9 @@ use oxc_allocator::Vec; -use crate::{doc::Doc, group, if_break, line, softline, ss, Prettier}; +use crate::{ + doc::{Doc, Group}, + group, if_break, line, softline, ss, Prettier, +}; use super::Format; @@ -36,7 +39,7 @@ pub(super) fn print_object_properties<'a, F: Format<'a>>( } parts.push(ss!("}")); - Doc::Group(parts) + Doc::Group(Group::new(parts, false)) }; content diff --git a/crates/oxc_prettier/src/format/statement.rs b/crates/oxc_prettier/src/format/statement.rs index 0607e4865..18fd502ae 100644 --- a/crates/oxc_prettier/src/format/statement.rs +++ b/crates/oxc_prettier/src/format/statement.rs @@ -1,6 +1,9 @@ use oxc_allocator::Vec; -use crate::{doc::Doc, hardline, Prettier}; +use crate::{ + doc::{Doc, Group}, + hardline, Prettier, +}; use oxc_span::GetSpan; use super::Format; @@ -17,7 +20,7 @@ pub(super) fn print_statement_sequence<'a, F: Format<'a> + GetSpan>( if remove_last_statement_hardline && i == stmts.len() - 1 { match docs { - Doc::Array(ref mut docs) | Doc::Group(ref mut docs) => { + Doc::Array(ref mut docs) | Doc::Group(Group { contents: ref mut docs, .. }) => { if matches!(docs.last(), Some(Doc::Hardline)) { docs.pop(); } diff --git a/crates/oxc_prettier/src/macros.rs b/crates/oxc_prettier/src/macros.rs index 3a1f9aecf..a5c07dd63 100644 --- a/crates/oxc_prettier/src/macros.rs +++ b/crates/oxc_prettier/src/macros.rs @@ -90,7 +90,7 @@ macro_rules! group { $( temp_vec.push($x); )* - Doc::Group(temp_vec) + Doc::Group($crate::doc::Group::new(temp_vec, false)) } }; } diff --git a/crates/oxc_prettier/src/printer/mod.rs b/crates/oxc_prettier/src/printer/mod.rs index 8210e1620..a285dbb4d 100644 --- a/crates/oxc_prettier/src/printer/mod.rs +++ b/crates/oxc_prettier/src/printer/mod.rs @@ -7,7 +7,10 @@ mod command; use std::collections::VecDeque; -use crate::{doc::Doc, PrettierOptions}; +use crate::{ + doc::{Doc, Group}, + PrettierOptions, +}; use self::command::{Command, Indent, Mode}; @@ -51,7 +54,7 @@ impl<'a> Printer<'a> { Doc::Str(s) => self.handle_str(s), Doc::Array(docs) => self.handle_array(indent, mode, docs), Doc::Indent(docs) => self.handle_indent(indent, mode, docs), - Doc::Group(docs) => self.handle_group(indent, mode, docs), + Doc::Group(group) => self.handle_group(indent, mode, group), Doc::IndentIfBreak(docs) => self.handle_indent_if_break(indent, mode, docs), Doc::Line => self.handle_line(indent, mode), Doc::Softline => self.handle_softline(indent, mode), @@ -78,25 +81,37 @@ impl<'a> Printer<'a> { ); } - fn handle_group(&mut self, indent: Indent, mode: Mode, docs: oxc_allocator::Vec<'a, Doc<'a>>) { + fn handle_group(&mut self, indent: Indent, mode: Mode, group: Group<'a>) { match mode { Mode::Flat => { // TODO: consider supporting `group mode` e.g. Break/Flat - self.cmds.extend( - docs.into_iter().rev().map(|doc| Command::new(indent, Mode::Flat, doc)), - ); + self.cmds.extend(group.contents.into_iter().rev().map(|doc| { + Command::new( + indent, + if group.should_break { Mode::Break } else { Mode::Flat }, + doc, + ) + })); } Mode::Break => { #[allow(clippy::cast_possible_wrap)] let remaining_width = (self.options.print_width as isize) - (self.pos as isize); - if self.fits(&docs, indent, remaining_width) { + if !group.should_break && self.fits(&group.contents, indent, remaining_width) { self.cmds.extend( - docs.into_iter().rev().map(|doc| Command::new(indent, Mode::Flat, doc)), + group + .contents + .into_iter() + .rev() + .map(|doc| Command::new(indent, Mode::Flat, doc)), ); } else { self.cmds.extend( - docs.into_iter().rev().map(|doc| Command::new(indent, Mode::Break, doc)), + group + .contents + .into_iter() + .rev() + .map(|doc| Command::new(indent, Mode::Break, doc)), ); } } @@ -167,10 +182,7 @@ impl<'a> Printer<'a> { Doc::Str(string) => { remaining_width -= string.len() as isize; } - Doc::IndentIfBreak(docs) - | Doc::Array(docs) - | Doc::Indent(docs) - | Doc::Group(docs) => { + Doc::IndentIfBreak(docs) | Doc::Array(docs) | Doc::Indent(docs) => { // Prepend docs to the queue for d in docs.iter().rev() { queue.push_front(d); @@ -180,6 +192,14 @@ impl<'a> Printer<'a> { remaining_width -= (self.options.tab_width * indent.length) as isize; } } + Doc::Group(group) => { + if group.should_break { + return false; + } + for d in group.contents.iter().rev() { + queue.push_front(d); + } + } // trying to fit on a single line, so we don't need to consider line breaks Doc::IfBreak { .. } | Doc::Softline => {} Doc::Line => remaining_width -= 1,