mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(prettier): add id on IfBreak and Group (#1551)
This commit is contained in:
parent
0e1abae757
commit
21dffec8be
11 changed files with 104 additions and 35 deletions
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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!("}"),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue