diff --git a/crates/oxc_prettier/src/doc.rs b/crates/oxc_prettier/src/doc.rs index 825d7ba3e..8fd1b5662 100644 --- a/crates/oxc_prettier/src/doc.rs +++ b/crates/oxc_prettier/src/doc.rs @@ -6,7 +6,7 @@ use oxc_allocator::{Allocator, Box, String, Vec}; use std::fmt; -use crate::{array, line, ss}; +use crate::{array, line, ss, GroupId}; #[derive(Debug)] pub enum Doc<'a> { @@ -37,7 +37,7 @@ pub enum Doc<'a> { /// `lineSuffix` buffers docs passed to it and flushes them before any new line. LineSuffix(Vec<'a, Doc<'a>>), /// Print something if the current `group` or the current element of `fill` breaks and something else if it doesn't. - IfBreak(Box<'a, Doc<'a>>), + IfBreak(IfBreak<'a>), /// This is an alternative type of group which behaves like text layout: /// it's going to add a break whenever the next element doesn't fit in the line anymore. /// The difference with `group` is that it's not going to break all the separators, just the ones that are at the end of lines. @@ -50,11 +50,16 @@ pub enum Doc<'a> { pub struct Group<'a> { pub contents: Vec<'a, Doc<'a>>, pub should_break: bool, + pub id: Option, } impl<'a> Group<'a> { pub fn new(contents: Vec<'a, Doc<'a>>, should_break: bool) -> Self { - Self { contents, should_break } + Self { contents, should_break, id: None } + } + pub fn with_id(mut self, id: u32) -> Self { + self.id = Some(id); + self } } @@ -90,6 +95,13 @@ impl<'a> Fill<'a> { } } +#[derive(Debug)] +pub struct IfBreak<'a> { + pub break_contents: Box<'a, Doc<'a>>, + pub flat_content: Box<'a, Doc<'a>>, + pub group_id: Option, +} + #[derive(Clone, Copy)] #[allow(unused)] pub enum Separator { @@ -190,6 +202,9 @@ fn print_doc_to_debug(doc: &Doc<'_>) -> std::string::String { } string.push_str("], { shouldBreak: "); string.push_str(&group.should_break.to_string()); + if let Some(id) = group.id { + string.push_str(&format!(", id: {id}")); + } string.push_str(" })"); } Doc::Line => { @@ -201,9 +216,15 @@ fn print_doc_to_debug(doc: &Doc<'_>) -> std::string::String { Doc::Hardline => { string.push_str("hardline"); } - Doc::IfBreak(break_contents) => { - string.push_str("ifBreak("); - string.push_str(&print_doc_to_debug(break_contents)); + Doc::IfBreak(if_break) => { + string.push_str(&format!( + "ifBreak({}, {})", + print_doc_to_debug(&if_break.break_contents), + print_doc_to_debug(&if_break.flat_content) + )); + if let Some(group_id) = if_break.group_id { + string.push_str(&format!("{{ groupId: {group_id} }}")); + } string.push(')'); } Doc::Fill(fill) => { diff --git a/crates/oxc_prettier/src/format/array.rs b/crates/oxc_prettier/src/format/array.rs index 854558825..fcfac9c61 100644 --- a/crates/oxc_prettier/src/format/array.rs +++ b/crates/oxc_prettier/src/format/array.rs @@ -82,6 +82,7 @@ pub(super) fn print_array<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Do (false, false) }; + let id = p.next_id(); let should_use_concise_formatting = array.is_concisely_printed(); let trailing_comma_fn = |p: &Prettier<'a>| { if !can_have_trailing_comma { @@ -89,7 +90,7 @@ pub(super) fn print_array<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Do } else if needs_forced_trailing_comma { ss!(",") } else if should_use_concise_formatting { - if_break!(p, ",") + if_break!(p, ",", "", Some(id)) } else { ss!("") } @@ -110,7 +111,7 @@ pub(super) fn print_array<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Do }; parts.push(group!(p, ss!("["), parts_inner, softline!(), ss!("]"))); let should_break = should_break(array); - Doc::Group(Group::new(parts, should_break)) + Doc::Group(Group::new(parts, should_break).with_id(id)) } fn print_empty_array_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { diff --git a/crates/oxc_prettier/src/format/binaryish.rs b/crates/oxc_prettier/src/format/binaryish.rs index 01612581c..502c29434 100644 --- a/crates/oxc_prettier/src/format/binaryish.rs +++ b/crates/oxc_prettier/src/format/binaryish.rs @@ -78,7 +78,7 @@ pub(super) fn print_binaryish_expression<'a>( if operator.is_binary() { parts.push(group!(p, ss!(operator.as_str()), Doc::Line, right.format(p))); - Doc::Group(Group { contents: parts, should_break: false }) + Doc::Group(Group::new(parts, false)) } else { parts.push(ss!(operator.as_str())); parts.push(Doc::Line); diff --git a/crates/oxc_prettier/src/format/call_expression.rs b/crates/oxc_prettier/src/format/call_expression.rs index 10be9029d..a3bef6101 100644 --- a/crates/oxc_prettier/src/format/call_expression.rs +++ b/crates/oxc_prettier/src/format/call_expression.rs @@ -64,7 +64,7 @@ pub(super) fn print_call_expression<'a>( parts.push(print_call_expression_arguments(p, expression)); - Doc::Group(Group { contents: parts, should_break: false }) + Doc::Group(Group::new(parts, false)) } fn print_call_expression_arguments<'a>( @@ -100,7 +100,7 @@ fn print_call_expression_arguments<'a>( if should_break { parts_inner.insert(0, Doc::Softline); parts.push(Doc::Indent(parts_inner)); - parts.push(if_break!(p, ",")); + parts.push(if_break!(p, ",", "", None)); parts.push(Doc::Softline); } else { parts.extend(parts_inner); diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index 8192c83ed..d0af9209e 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -471,10 +471,7 @@ impl<'a> Format<'a> for SwitchCase<'a> { if is_only_one_block_statement { parts.extend(consequent_parts); } else { - parts.push(indent!( - p, - Doc::Group(Group { contents: consequent_parts, should_break: false }) - )); + parts.push(indent!(p, Doc::Group(Group::new(consequent_parts, false)))); } } diff --git a/crates/oxc_prettier/src/format/module.rs b/crates/oxc_prettier/src/format/module.rs index d52a63093..f469d62e7 100644 --- a/crates/oxc_prettier/src/format/module.rs +++ b/crates/oxc_prettier/src/format/module.rs @@ -105,7 +105,7 @@ pub fn print_module_specifiers<'a, T: Format<'a>>( if p.options.bracket_spacing { line!() } else { softline!() }, Doc::Array(p.join(Separator::CommaLine, docs)) ], - if_break!(p, if p.should_print_es5_comma() { "," } else { "" }), + if_break!(p, if p.should_print_es5_comma() { "," } else { "" }, "", None), if p.options.bracket_spacing { line!() } else { softline!() }, ss!("}"), ]); diff --git a/crates/oxc_prettier/src/format/object.rs b/crates/oxc_prettier/src/format/object.rs index 2464b5fe7..a76f539dd 100644 --- a/crates/oxc_prettier/src/format/object.rs +++ b/crates/oxc_prettier/src/format/object.rs @@ -57,7 +57,7 @@ pub(super) fn print_object_properties<'a, F: Format<'a> + GetSpan>( } parts.push(Doc::Indent(indent_parts)); - parts.push(if_break!(p, ",")); + parts.push(if_break!(p, ",", "", None)); if p.options.bracket_spacing { parts.push(Doc::Line); diff --git a/crates/oxc_prettier/src/lib.rs b/crates/oxc_prettier/src/lib.rs index bba555c3f..f5fb29361 100644 --- a/crates/oxc_prettier/src/lib.rs +++ b/crates/oxc_prettier/src/lib.rs @@ -23,6 +23,19 @@ use crate::{doc::Doc, format::Format, printer::Printer}; pub use crate::options::{ArrowParens, EndOfLine, PrettierOptions, QuoteProps, TrailingComma}; +type GroupId = u32; +#[derive(Default)] +struct GroupIdBuilder { + id: GroupId, +} + +impl GroupIdBuilder { + pub fn next_id(&mut self) -> GroupId { + self.id += 1; + self.id + } +} + pub struct Prettier<'a> { allocator: &'a Allocator, @@ -36,6 +49,8 @@ pub struct Prettier<'a> { /// The stack of AST Nodes /// See nodes: Vec>, + + group_id_builder: GroupIdBuilder, } impl<'a> DocBuilder<'a> for Prettier<'a> { @@ -58,6 +73,7 @@ impl<'a> Prettier<'a> { options, trivias: trivias.into_iter().peekable(), nodes: vec![], + group_id_builder: GroupIdBuilder::default(), } } @@ -178,4 +194,8 @@ impl<'a> Prettier<'a> { let idx2 = self.skip_newline(idx, true); idx != idx2 } + + fn next_id(&mut self) -> GroupId { + self.group_id_builder.next_id() + } } diff --git a/crates/oxc_prettier/src/macros.rs b/crates/oxc_prettier/src/macros.rs index be16ccbb3..abdc3986d 100644 --- a/crates/oxc_prettier/src/macros.rs +++ b/crates/oxc_prettier/src/macros.rs @@ -100,9 +100,13 @@ macro_rules! group { #[macro_export] macro_rules! if_break { - ($p:ident, $s:expr) => {{ - use $crate::doc::DocBuilder; - Doc::IfBreak($p.boxed(Doc::Str($s))) + ($p:ident, $s:expr, $flat:expr, $group_id:expr) => {{ + use $crate::doc::{DocBuilder, IfBreak}; + Doc::IfBreak(IfBreak { + break_contents: $p.boxed(Doc::Str($s)), + flat_content: $p.boxed(Doc::Str($flat)), + group_id: $group_id, + }) }}; } diff --git a/crates/oxc_prettier/src/printer/mod.rs b/crates/oxc_prettier/src/printer/mod.rs index 800f7b8d5..71f38475e 100644 --- a/crates/oxc_prettier/src/printer/mod.rs +++ b/crates/oxc_prettier/src/printer/mod.rs @@ -6,11 +6,11 @@ mod command; use oxc_allocator::Allocator; -use std::collections::VecDeque; +use std::collections::{HashMap, VecDeque}; use crate::{ - doc::{Doc, DocBuilder, Fill}, - PrettierOptions, + doc::{Doc, DocBuilder, Fill, IfBreak}, + GroupId, PrettierOptions, }; use self::command::{Command, Indent, Mode}; @@ -27,6 +27,7 @@ pub struct Printer<'a> { cmds: Vec>, line_suffix: Vec>, + group_mode_map: HashMap, // states new_line: &'static str, @@ -58,6 +59,7 @@ impl<'a> Printer<'a> { pos: 0, cmds, line_suffix: vec![], + group_mode_map: HashMap::new(), new_line: options.end_of_line.as_str(), allocator, } @@ -82,7 +84,7 @@ impl<'a> Printer<'a> { } group.should_break } - Doc::IfBreak(d) => Self::propagate_breaks(d), + Doc::IfBreak(d) => Self::propagate_breaks(&mut d.break_contents), Doc::Array(arr) | Doc::Indent(arr) | Doc::IndentIfBreak(arr) => { arr.iter_mut().any(|doc| Self::propagate_breaks(doc)) } @@ -107,7 +109,7 @@ impl<'a> Printer<'a> { Doc::Softline => self.handle_softline(indent, mode), Doc::Hardline => self.handle_line(indent, Mode::Break), Doc::LineSuffix(docs) => self.handle_line_suffix(indent, mode, docs), - Doc::IfBreak(doc) => self.handle_if_break(doc.unbox(), indent, mode), + Doc::IfBreak(if_break) => self.handle_if_break(if_break, indent, mode), Doc::Fill(fill) => self.handle_fill(indent, mode, fill), Doc::BreakParent => {} // No op } @@ -152,6 +154,7 @@ impl<'a> Printer<'a> { return; }; let should_break = group.should_break; + let id = group.id; let cmd = Command::new(indent, Mode::Flat, doc); if !should_break && self.fits(&cmd, remaining_width) { self.cmds.push(Command::new(indent, Mode::Flat, cmd.doc)); @@ -167,6 +170,11 @@ impl<'a> Printer<'a> { .map(|doc| Command::new(indent, Mode::Break, doc)), ); } + + if let Some(id) = id { + let Some(mode) = self.cmds.last().map(|cmd| cmd.mode) else { return }; + self.group_mode_map.insert(id, mode); + } } } } @@ -226,9 +234,18 @@ impl<'a> Printer<'a> { self.line_suffix.push(Command { indent, mode, doc: Doc::Array(docs) }); } - fn handle_if_break(&mut self, doc: Doc<'a>, indent: Indent, mode: Mode) { - if mode == Mode::Break { - self.cmds.push(Command::new(indent, Mode::Break, doc)); + fn handle_if_break(&mut self, if_break: IfBreak<'a>, indent: Indent, mode: Mode) { + let IfBreak { break_contents, flat_content, group_id } = if_break; + let group_mode = group_id.map_or(Some(mode), |id| self.group_mode_map.get(&id).copied()); + + match group_mode { + Some(Mode::Flat) => { + self.cmds.push(Command::new(indent, Mode::Flat, flat_content.unbox())); + } + Some(Mode::Break) => { + self.cmds.push(Command::new(indent, Mode::Break, break_contents.unbox())); + } + None => {} } } @@ -339,10 +356,18 @@ impl<'a> Printer<'a> { queue.push_front((mode, d)); } } - Doc::IfBreak(doc) => { - if is_break { - queue.push_front((mode, doc)); - } + Doc::IfBreak(if_break_doc) => { + let group_mode = if_break_doc + .group_id + .map_or(mode, |id| *self.group_mode_map.get(&id).unwrap_or(&Mode::Flat)); + + let contents = if group_mode.is_break() { + &if_break_doc.break_contents + } else { + &if_break_doc.flat_content + }; + + queue.push_front((mode, contents)); } Doc::Line => { if is_break { diff --git a/tasks/prettier_conformance/prettier.snap.md b/tasks/prettier_conformance/prettier.snap.md index 3b68b9d2a..4d6ac0821 100644 --- a/tasks/prettier_conformance/prettier.snap.md +++ b/tasks/prettier_conformance/prettier.snap.md @@ -1,4 +1,8 @@ +<<<<<<< HEAD Compatibility: 173/591 (29.27%) +======= +Compatibility: 171/591 (28.93%) +>>>>>>> 169f7a38 (feat(prettier): add id on `IfBreak` and `Group`) # Failed @@ -7,14 +11,11 @@ Compatibility: 173/591 (29.27%) * arrays/issue-10159.js * arrays/nested.js * arrays/numbers-in-args.js -* arrays/numbers-in-assignment.js * arrays/numbers-negative-comment-after-minus.js * arrays/numbers-negative.js -* arrays/numbers-trailing-comma.js * arrays/numbers-with-holes.js * arrays/numbers-with-trailing-comments.js * arrays/numbers-with-tricky-comments.js -* arrays/numbers1.js * arrays/numbers2.js * arrays/numbers3.js * arrays/preserve_empty_lines.js