diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 300123c84..12138f960 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -885,6 +885,16 @@ pub struct ObjectAssignmentTarget<'a> { pub rest: Option>, } +impl<'a> ObjectAssignmentTarget<'a> { + pub fn is_empty(&self) -> bool { + self.properties.is_empty() && self.rest.is_none() + } + + pub fn len(&self) -> usize { + self.properties.len() + usize::from(self.rest.is_some()) + } +} + #[derive(Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize), serde(untagged))] pub enum AssignmentTargetMaybeDefault<'a> { @@ -1441,6 +1451,10 @@ impl<'a> ObjectPattern<'a> { pub fn is_empty(&self) -> bool { self.properties.is_empty() && self.rest.is_none() } + + pub fn len(&self) -> usize { + self.properties.len() + usize::from(self.rest.is_some()) + } } #[derive(Debug, Hash)] @@ -1467,6 +1481,10 @@ impl<'a> ArrayPattern<'a> { pub fn is_empty(&self) -> bool { self.elements.is_empty() && self.rest.is_none() } + + pub fn len(&self) -> usize { + self.elements.len() + usize::from(self.rest.is_some()) + } } #[derive(Debug, Hash)] diff --git a/crates/oxc_prettier/src/format/array.rs b/crates/oxc_prettier/src/format/array.rs index cfd38355a..1a5e74dc7 100644 --- a/crates/oxc_prettier/src/format/array.rs +++ b/crates/oxc_prettier/src/format/array.rs @@ -182,6 +182,7 @@ fn print_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { if let Some(rest) = &array_pat.rest { parts.push(ss!(",")); parts.push(line!()); + parts.push(ss!("...")); parts.push(rest.format(p)); } } diff --git a/crates/oxc_prettier/src/format/function_parameters.rs b/crates/oxc_prettier/src/format/function_parameters.rs index 86b8be96e..eeebfed25 100644 --- a/crates/oxc_prettier/src/format/function_parameters.rs +++ b/crates/oxc_prettier/src/format/function_parameters.rs @@ -20,7 +20,9 @@ pub(super) fn print_function_parameters<'a>( } if let Some(rest) = ¶ms.rest { - parts.push(ss!(", ")); + if !params.items.is_empty() { + parts.push(ss!(", ")); + } parts.push(rest.format(p)); } diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index 677df4f6f..d475c9231 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -1455,11 +1455,7 @@ impl<'a> Format<'a> for ArrayExpression<'a> { impl<'a> Format<'a> for ObjectExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ObjectExpression, { - object::print_object_properties( - p, - &ObjectLike::ObjectExpression(self), - &self.properties, - ) + object::print_object_properties(p, ObjectLike::Expression(self)) }) } } @@ -1698,11 +1694,7 @@ impl<'a> Format<'a> for AssignmentTargetMaybeDefault<'a> { impl<'a> Format<'a> for ObjectAssignmentTarget<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - object::print_object_properties( - p, - &ObjectLike::ObjectAssignmentTarget(self), - &self.properties, - ) + object::print_object_properties(p, ObjectLike::AssignmentTarget(self)) } } @@ -2074,7 +2066,7 @@ impl<'a> Format<'a> for BindingPattern<'a> { impl<'a> Format<'a> for ObjectPattern<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - object::print_object_properties(p, &ObjectLike::ObjectPattern(self), &self.properties) + object::print_object_properties(p, ObjectLike::Pattern(self)) } } diff --git a/crates/oxc_prettier/src/format/object.rs b/crates/oxc_prettier/src/format/object.rs index 7087a8944..0c24f3cea 100644 --- a/crates/oxc_prettier/src/format/object.rs +++ b/crates/oxc_prettier/src/format/object.rs @@ -1,6 +1,5 @@ -use oxc_allocator::Vec; use oxc_ast::ast::{ObjectAssignmentTarget, ObjectExpression, ObjectPattern}; -use oxc_span::{GetSpan, Span}; +use oxc_span::Span; use crate::{ doc::{Doc, DocBuilder, Group}, @@ -9,62 +8,100 @@ use crate::{ use super::{misc, Format}; -#[allow(clippy::enum_variant_names)] +#[derive(Debug, Clone, Copy)] pub enum ObjectLike<'a, 'b> { - ObjectExpression(&'b ObjectExpression<'a>), - ObjectAssignmentTarget(&'b ObjectAssignmentTarget<'a>), - ObjectPattern(&'b ObjectPattern<'a>), + Expression(&'b ObjectExpression<'a>), + AssignmentTarget(&'b ObjectAssignmentTarget<'a>), + Pattern(&'b ObjectPattern<'a>), } -impl ObjectLike<'_, '_> { - fn is_object_pattern(&self) -> bool { - matches!(self, ObjectLike::ObjectPattern(_)) - } -} - -impl ObjectLike<'_, '_> { - pub fn span(&self) -> Span { +impl<'a, 'b> ObjectLike<'a, 'b> { + fn len(&self) -> usize { match self { - ObjectLike::ObjectExpression(object) => object.span, - ObjectLike::ObjectAssignmentTarget(object) => object.span, - ObjectLike::ObjectPattern(object) => object.span, + ObjectLike::Expression(object) => object.properties.len(), + ObjectLike::AssignmentTarget(object) => object.properties.len(), + ObjectLike::Pattern(object) => object.properties.len(), + } + } + + fn is_empty(&self) -> bool { + match self { + ObjectLike::Expression(object) => object.properties.is_empty(), + ObjectLike::AssignmentTarget(object) => object.is_empty(), + ObjectLike::Pattern(object) => object.is_empty(), + } + } + + fn is_object_pattern(&self) -> bool { + matches!(self, ObjectLike::Pattern(_)) + } + + fn span(&self) -> Span { + match self { + ObjectLike::Expression(object) => object.span, + ObjectLike::AssignmentTarget(object) => object.span, + ObjectLike::Pattern(object) => object.span, + } + } + + fn iter(&'b self, p: &'b mut Prettier<'a>) -> Box> + 'b> { + match self { + ObjectLike::Expression(object) => { + Box::new(object.properties.iter().map(|prop| prop.format(p))) + } + ObjectLike::AssignmentTarget(object) => { + Box::new(object.properties.iter().map(|prop| prop.format(p))) + } + ObjectLike::Pattern(object) => { + Box::new(object.properties.iter().map(|prop| prop.format(p))) + } } } } -pub(super) fn print_object_properties<'a, F: Format<'a> + GetSpan>( +pub(super) fn print_object_properties<'a>( p: &mut Prettier<'a>, - object: &ObjectLike<'a, '_>, - properties: &Vec<'a, F>, + object: ObjectLike<'a, '_>, ) -> Doc<'a> { let left_brace = ss!("{"); let right_brace = ss!("}"); - let content = if properties.is_empty() { + let content = if object.is_empty() { group![p, left_brace, softline!(), right_brace] } else { let mut parts = p.vec(); parts.push(ss!("{")); - - let mut indent_parts = p.vec(); - indent_parts.push(if p.options.bracket_spacing { line!() } else { softline!() }); - for (i, prop) in properties.iter().enumerate() { - indent_parts.push(prop.format(p)); - if i < properties.len() - 1 { - indent_parts.push(Doc::Str(",")); - indent_parts.push(line!()); + parts.push(Doc::Indent({ + let len = object.len(); + let mut indent_parts = p.vec(); + indent_parts.push(if p.options.bracket_spacing { line!() } else { softline!() }); + for (i, doc) in object.iter(p).enumerate() { + indent_parts.push(doc); + if i < len - 1 { + indent_parts.push(ss!(",")); + indent_parts.push(line!()); + } } - } - - parts.push(Doc::Indent(indent_parts)); + match object { + ObjectLike::Expression(object) => {} + ObjectLike::AssignmentTarget(object) => { + if let Some(rest) = &object.rest { + indent_parts.push(ss!("...")); + indent_parts.push(rest.format(p)); + } + } + ObjectLike::Pattern(object) => { + if let Some(rest) = &object.rest { + indent_parts.push(ss!(",")); + indent_parts.push(line!()); + indent_parts.push(rest.format(p)); + } + } + } + indent_parts + })); parts.push(if_break!(p, ",", "", None)); - - if p.options.bracket_spacing { - parts.push(line!()); - } else { - parts.push(softline!()); - } - + parts.push(if p.options.bracket_spacing { line!() } else { softline!() }); parts.push(ss!("}")); if object.is_object_pattern() {