feat(prettier): add id on IfBreak and Group (#1551)

This commit is contained in:
Wenzhe Wang 2023-11-27 22:53:32 +08:00 committed by GitHub
parent 0e1abae757
commit 21dffec8be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 104 additions and 35 deletions

View file

@ -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<u32>,
}
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<GroupId>,
}
#[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) => {

View file

@ -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> {

View file

@ -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);

View file

@ -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);

View file

@ -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))));
}
}

View file

@ -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!("}"),
]);

View file

@ -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);

View file

@ -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 <https://github.com/prettier/prettier/blob/main/src/common/ast-path.js>
nodes: Vec<AstKind<'a>>,
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()
}
}

View file

@ -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,
})
}};
}

View file

@ -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<Command<'a>>,
line_suffix: Vec<Command<'a>>,
group_mode_map: HashMap<GroupId, Mode>,
// 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 {

View file

@ -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