From cda0b97adebca23a9532bc41797088a617a79b8c Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 28 Nov 2023 02:29:32 +0000 Subject: [PATCH] feat(prettier) use groupId in fluid assignments (#1560) --- crates/oxc_prettier/src/doc.rs | 34 +++++++--- crates/oxc_prettier/src/format/assignment.rs | 25 +++++--- crates/oxc_prettier/src/printer/mod.rs | 65 ++++++++++---------- tasks/prettier_conformance/prettier.snap.md | 3 +- 4 files changed, 77 insertions(+), 50 deletions(-) diff --git a/crates/oxc_prettier/src/doc.rs b/crates/oxc_prettier/src/doc.rs index adebd9c24..62d40ff10 100644 --- a/crates/oxc_prettier/src/doc.rs +++ b/crates/oxc_prettier/src/doc.rs @@ -15,7 +15,7 @@ pub enum Doc<'a> { Array(Vec<'a, Doc<'a>>), /// Increase the level of indentation. Indent(Vec<'a, Doc<'a>>), - IndentIfBreak(Vec<'a, Doc<'a>>), + IndentIfBreak(IndentIfBreak<'a>), /// Mark a group of items which the printer should try to fit on one line. /// This is the basic command to tell the printer when to break. /// Groups are usually nested, and the printer will try to fit everything on one line, @@ -77,18 +77,33 @@ impl Line { pub struct Group<'a> { pub contents: Vec<'a, Doc<'a>>, pub should_break: bool, - pub id: 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 } } - pub fn with_id(mut self, id: u32) -> Self { + pub fn with_id(mut self, id: GroupId) -> Self { self.id = Some(id); self } } +#[derive(Debug)] +pub struct IndentIfBreak<'a> { + pub contents: Vec<'a, Doc<'a>>, + pub group_id: Option, +} + +impl<'a> IndentIfBreak<'a> { + pub fn new(contents: Vec<'a, Doc<'a>>) -> Self { + Self { contents, group_id: None } + } + pub fn with_id(mut self, id: GroupId) -> Self { + self.group_id = Some(id); + self + } +} #[derive(Debug)] pub struct Fill<'a> { @@ -212,16 +227,21 @@ fn print_doc_to_debug(doc: &Doc<'_>) -> std::string::String { } string.push_str("])"); } - Doc::IndentIfBreak(contents) => { + Doc::IndentIfBreak(indent_if_break) => { string.push_str("indentIfBreak("); string.push_str("[\n"); - for (idx, doc) in contents.iter().enumerate() { + for (idx, doc) in indent_if_break.contents.iter().enumerate() { string.push_str(&print_doc_to_debug(doc)); - if idx != contents.len() - 1 { + if idx != indent_if_break.contents.len() - 1 { string.push_str(", "); } } - string.push_str("]) \n"); + + if let Some(id) = indent_if_break.group_id { + string.push_str(&format!(", {{id: {id}}}")); + } + + string.push_str("])"); } Doc::Group(group) => { string.push_str("group([\n"); diff --git a/crates/oxc_prettier/src/format/assignment.rs b/crates/oxc_prettier/src/format/assignment.rs index 73dcbb0df..84527f56f 100644 --- a/crates/oxc_prettier/src/format/assignment.rs +++ b/crates/oxc_prettier/src/format/assignment.rs @@ -8,7 +8,7 @@ use oxc_ast::{ use crate::{ array, - doc::{Doc, DocBuilder}, + doc::{Doc, DocBuilder, Group, IndentIfBreak}, group, indent, line, ss, Format, Prettier, }; @@ -67,14 +67,21 @@ fn print_assignment<'a>( } // First break right-hand side, then after operator Layout::Fluid => { - group!( - p, - group!(p, left_doc), - op, - group!(p, indent!(p, line!())), - // TODO: wrap `right_doc` in indent_if_break!() when we have support for group IDs. - right_doc - ) + let group_id = p.next_id(); + + let after_op = { + let mut parts = p.vec(); + parts.push(indent!(p, line!())); + Doc::Group(Group::new(parts, false).with_id(group_id)) + }; + + let right_doc = { + let mut parts = p.vec(); + parts.push(group!(p, right_doc)); + Doc::IndentIfBreak(IndentIfBreak::new(parts).with_id(group_id)) + }; + + group!(p, group!(p, left_doc), op, after_op, right_doc) } Layout::BreakLhs => { group!(p, left_doc, op, ss!(" "), group!(p, right_doc)) diff --git a/crates/oxc_prettier/src/printer/mod.rs b/crates/oxc_prettier/src/printer/mod.rs index dc3f9bb75..231e301de 100644 --- a/crates/oxc_prettier/src/printer/mod.rs +++ b/crates/oxc_prettier/src/printer/mod.rs @@ -9,7 +9,7 @@ use oxc_allocator::Allocator; use std::collections::{HashMap, VecDeque}; use crate::{ - doc::{Doc, DocBuilder, Fill, IfBreak, Line}, + doc::{Doc, DocBuilder, Fill, IfBreak, IndentIfBreak, Line}, GroupId, PrettierOptions, }; @@ -122,28 +122,24 @@ impl<'a> Printer<'a> { fn handle_group(&mut self, indent: Indent, mode: Mode, doc: Doc<'a>) { match mode { Mode::Flat => { - let Doc::Group(group) = doc else { - return; - }; + let Doc::Group(group) = doc else { unreachable!() }; self.cmds.extend(group.contents.into_iter().rev().map(|doc| { Command::new(indent, if group.should_break { Mode::Break } else { mode }, doc) })); + + self.set_group_mode_from_last_cmd(group.id); } Mode::Break => { #[allow(clippy::cast_possible_wrap)] let remaining_width = self.remaining_width(); - let Doc::Group(group) = &doc else { - return; - }; + let Doc::Group(group) = &doc else { unreachable!() }; let should_break = group.should_break; - let id = group.id; + let group_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)); } else { - let Doc::Group(group) = cmd.doc else { - return; - }; + let Doc::Group(group) = cmd.doc else { unreachable!() }; self.cmds.extend( group .contents @@ -152,34 +148,29 @@ 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); - } + self.set_group_mode_from_last_cmd(group_id); } } } - fn handle_indent_if_break( - &mut self, - indent: Indent, - mode: Mode, - docs: oxc_allocator::Vec<'a, Doc<'a>>, - ) { - match mode { - Mode::Flat => { - self.cmds.extend( - docs.into_iter().rev().map(|doc| Command::new(indent, Mode::Flat, doc)), - ); + fn handle_indent_if_break(&mut self, indent: Indent, mode: Mode, doc: IndentIfBreak<'a>) { + let IndentIfBreak { contents, group_id } = doc; + 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 + .extend(contents.into_iter().rev().map(|doc| Command::new(indent, mode, doc))); } - Mode::Break => { + Some(Mode::Break) => { self.cmds.extend( - docs.into_iter() + contents + .into_iter() .rev() - .map(|doc| Command::new(Indent::new(indent.length + 1), Mode::Break, doc)), + .map(|doc| Command::new(Indent::new(indent.length + 1), mode, doc)), ); } + None => {} } } @@ -341,6 +332,12 @@ impl<'a> Printer<'a> { } } + fn set_group_mode_from_last_cmd(&mut self, id: Option) { + let Some(id) = id else { return }; + let Some(mode) = self.cmds.last().map(|cmd| cmd.mode) else { return }; + self.group_mode_map.insert(id, mode); + } + #[allow(clippy::cast_possible_wrap)] fn fits(&self, next: &Command<'a>, width: isize) -> bool { let mut remaining_width = width; @@ -353,7 +350,9 @@ impl<'a> Printer<'a> { Doc::Str(string) => { remaining_width -= string.len() as isize; } - Doc::IndentIfBreak(docs) | Doc::Indent(docs) | Doc::Array(docs) => { + Doc::IndentIfBreak(IndentIfBreak { contents: docs, .. }) + | Doc::Indent(docs) + | Doc::Array(docs) => { // Prepend docs to the queue for d in docs.iter().rev() { queue.push_front((mode, d)); @@ -425,7 +424,9 @@ impl<'a> Printer<'a> { group.should_break } Doc::IfBreak(d) => Self::propagate_breaks(&mut d.break_contents), - Doc::Array(arr) | Doc::Indent(arr) | Doc::IndentIfBreak(arr) => { + Doc::Array(arr) + | Doc::Indent(arr) + | Doc::IndentIfBreak(IndentIfBreak { contents: arr, .. }) => { arr.iter_mut().any(|doc| Self::propagate_breaks(doc)) } _ => false, diff --git a/tasks/prettier_conformance/prettier.snap.md b/tasks/prettier_conformance/prettier.snap.md index 61b7d45c5..2eb9a6731 100644 --- a/tasks/prettier_conformance/prettier.snap.md +++ b/tasks/prettier_conformance/prettier.snap.md @@ -1,4 +1,4 @@ -Compatibility: 177/591 (29.95%) +Compatibility: 178/591 (30.12%) # Failed @@ -56,7 +56,6 @@ Compatibility: 177/591 (29.95%) * assignment/chain.js * assignment/discussion-15196.js * assignment/issue-10218.js -* assignment/issue-1419.js * assignment/issue-15534.js * assignment/issue-2482-2.js * assignment/issue-2540.js