From 522cf294890eb651f7eb2c21735abee0b6e2d262 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 28 Nov 2023 02:54:33 +0000 Subject: [PATCH] feat(prettier) improve class printing (#1565) --- crates/oxc_ast/src/ast_kind.rs | 3 + crates/oxc_prettier/src/format/assignment.rs | 31 +++- crates/oxc_prettier/src/format/block.rs | 2 + crates/oxc_prettier/src/format/class.rs | 144 ++++++++++++++++++- crates/oxc_prettier/src/format/mod.rs | 10 +- tasks/prettier_conformance/prettier.snap.md | 6 +- 6 files changed, 177 insertions(+), 19 deletions(-) diff --git a/crates/oxc_ast/src/ast_kind.rs b/crates/oxc_ast/src/ast_kind.rs index f89af714c..3506b27cb 100644 --- a/crates/oxc_ast/src/ast_kind.rs +++ b/crates/oxc_ast/src/ast_kind.rs @@ -96,6 +96,7 @@ pub enum AstKind<'a> { FormalParameter(&'a FormalParameter<'a>), Class(&'a Class<'a>), + ClassBody(&'a ClassBody<'a>), ClassHeritage(&'a Expression<'a>), StaticBlock(&'a StaticBlock<'a>), PropertyDefinition(&'a PropertyDefinition<'a>), @@ -398,6 +399,7 @@ impl<'a> GetSpan for AstKind<'a> { Self::FormalParameter(x) => x.span, Self::Class(x) => x.span, + Self::ClassBody(x) => x.span, Self::ClassHeritage(x) => x.span(), Self::StaticBlock(x) => x.span, Self::PropertyDefinition(x) => x.span, @@ -569,6 +571,7 @@ impl<'a> AstKind<'a> { c.id.as_ref().map_or_else(|| "", |id| id.name.as_str()) ) .into(), + Self::ClassBody(_) => "ClassBody".into(), Self::ClassHeritage(_) => "ClassHeritage".into(), Self::StaticBlock(_) => "StaticBlock".into(), Self::PropertyDefinition(_) => "PropertyDefinition".into(), diff --git a/crates/oxc_prettier/src/format/assignment.rs b/crates/oxc_prettier/src/format/assignment.rs index 84527f56f..2e003b54e 100644 --- a/crates/oxc_prettier/src/format/assignment.rs +++ b/crates/oxc_prettier/src/format/assignment.rs @@ -1,7 +1,8 @@ use oxc_ast::{ ast::{ - AssignmentExpression, AssignmentTarget, AssignmentTargetPattern, AssignmentTargetProperty, - BindingPatternKind, Expression, Statement, VariableDeclarator, + AccessorProperty, AssignmentExpression, AssignmentTarget, AssignmentTargetPattern, + AssignmentTargetProperty, BindingPatternKind, Expression, PropertyDefinition, Statement, + VariableDeclarator, }, AstKind, }; @@ -12,6 +13,8 @@ use crate::{ group, indent, line, ss, Format, Prettier, }; +use super::class::ClassMemberish; + pub(super) fn print_assignment_expression<'a>( p: &mut Prettier<'a>, assignment_expr: &AssignmentExpression<'a>, @@ -41,12 +44,27 @@ pub(super) fn print_variable_declarator<'a>( } #[derive(Debug, Clone, Copy)] -enum AssignmentLikeNode<'a, 'b> { +pub(super) enum AssignmentLikeNode<'a, 'b> { AssignmentExpression(&'b AssignmentExpression<'a>), VariableDeclarator(&'b VariableDeclarator<'a>), + PropertyDefinition(&'b PropertyDefinition<'a>), + AccessorProperty(&'b AccessorProperty<'a>), } -fn print_assignment<'a>( +impl<'a, 'b> From> for AssignmentLikeNode<'a, 'b> { + fn from(class_memberish: ClassMemberish<'a, 'b>) -> Self { + match class_memberish { + ClassMemberish::PropertyDefinition(property_def) => { + Self::PropertyDefinition(property_def) + } + ClassMemberish::AccessorProperty(accessor_prop) => { + Self::AccessorProperty(accessor_prop) + } + } + } +} + +pub(super) fn print_assignment<'a>( p: &mut Prettier<'a>, node: AssignmentLikeNode<'a, '_>, left_doc: Doc<'a>, @@ -239,6 +257,7 @@ fn is_complex_destructuring(expr: &AssignmentLikeNode) -> bool { false } + _ => false, } } @@ -258,7 +277,9 @@ fn is_arrow_function_variable_declarator(expr: &AssignmentLikeNode) -> bool { } false } - AssignmentLikeNode::AssignmentExpression(_) => false, + AssignmentLikeNode::AssignmentExpression(_) + | AssignmentLikeNode::PropertyDefinition(_) + | AssignmentLikeNode::AccessorProperty(_) => false, } } diff --git a/crates/oxc_prettier/src/format/block.rs b/crates/oxc_prettier/src/format/block.rs index fb971bcb6..4ee14059c 100644 --- a/crates/oxc_prettier/src/format/block.rs +++ b/crates/oxc_prettier/src/format/block.rs @@ -36,6 +36,8 @@ pub(super) fn print_block<'a>( | AstKind::ForStatement(_) | AstKind::WhileStatement(_) | AstKind::DoWhileStatement(_) + | AstKind::MethodDefinition(_) + | AstKind::PropertyDefinition(_) ) || (matches!(parent, AstKind::CatchClause(_)) && !matches!(p.parent_parent_kind(), Some(AstKind::TryStatement(stmt)) if stmt.finalizer.is_some())) || matches!(p.current_kind(), AstKind::StaticBlock(_))) diff --git a/crates/oxc_prettier/src/format/class.rs b/crates/oxc_prettier/src/format/class.rs index 6140e1d66..d9516c6cb 100644 --- a/crates/oxc_prettier/src/format/class.rs +++ b/crates/oxc_prettier/src/format/class.rs @@ -3,9 +3,12 @@ use oxc_ast::ast::*; use crate::{ array, doc::{Doc, DocBuilder}, + format::assignment, hardline, ss, Format, Prettier, }; +use super::assignment::AssignmentLikeNode; + pub(super) fn print_class<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a> { let mut parts = p.vec(); parts.push(ss!("class ")); @@ -25,25 +28,156 @@ pub(super) fn print_class<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a } pub(super) fn print_class_body<'a>(p: &mut Prettier<'a>, class_body: &ClassBody<'a>) -> Doc<'a> { - let mut inner_parts = p.vec(); + let mut parts_inner = p.vec(); - for class_element in &class_body.body { - inner_parts.push(class_element.format(p)); + for (i, node) in class_body.body.iter().enumerate() { + parts_inner.push(node.format(p)); + + if !p.options.semi + && matches!( + node, + ClassElement::PropertyDefinition(_) + | ClassElement::AccessorProperty(_) + | ClassElement::TSAbstractPropertyDefinition(_) + ) + { + parts_inner.push(ss!(";")); + } + + if i < class_body.body.len() - 1 { + parts_inner.extend(hardline!()); + + // TODO: if the next line is empty, add another hardline + } } + // TODO: if there are any dangling comments, print them + let mut parts = p.vec(); + // TODO is class_body.len() != 0, print hardline after heritage + parts.push(ss!("{")); - if !inner_parts.is_empty() { + if !parts_inner.is_empty() { let indent = { let mut parts = p.vec(); parts.extend(hardline!()); - parts.push(Doc::Array(inner_parts)); + parts.push(Doc::Array(parts_inner)); Doc::Indent(parts) }; parts.push(array![p, indent]); parts.extend(hardline!()); } + parts.push(ss!("}")); Doc::Array(parts) } + +pub enum ClassMemberish<'a, 'b> { + PropertyDefinition(&'b PropertyDefinition<'a>), + AccessorProperty(&'b AccessorProperty<'a>), +} + +impl<'a, 'b> ClassMemberish<'a, 'b> { + fn format_key(&self, p: &mut Prettier<'a>) -> Doc<'a> { + match self { + ClassMemberish::PropertyDefinition(property_definition) => { + property_definition.key.format(p) + } + ClassMemberish::AccessorProperty(accessor_property) => accessor_property.key.format(p), + } + } + + fn decorators(&self) -> Option<&oxc_allocator::Vec>> { + match self { + ClassMemberish::PropertyDefinition(property_definition) => { + Some(&property_definition.decorators) + } + + ClassMemberish::AccessorProperty(accessor_property) => None, + } + } + + fn is_static(&self) -> bool { + match self { + ClassMemberish::PropertyDefinition(property_definition) => property_definition.r#static, + ClassMemberish::AccessorProperty(accessor_property) => accessor_property.r#static, + } + } + fn is_override(&self) -> bool { + match self { + ClassMemberish::PropertyDefinition(property_definition) => { + property_definition.r#override + } + ClassMemberish::AccessorProperty(accessor_property) => false, + } + } + fn is_readonly(&self) -> bool { + match self { + ClassMemberish::PropertyDefinition(property_definition) => property_definition.readonly, + ClassMemberish::AccessorProperty(_) => false, + } + } + + fn right_expr(&self) -> Option<&Expression<'a>> { + match self { + ClassMemberish::PropertyDefinition(property_definition) => { + property_definition.value.as_ref() + } + ClassMemberish::AccessorProperty(_) => None, + } + } +} + +pub(super) fn print_class_property<'a>( + p: &mut Prettier<'a>, + node: &ClassMemberish<'a, '_>, +) -> Doc<'a> { + let mut parts = p.vec(); + + if node.decorators().is_some_and(|x| !x.is_empty()) { + // TODO: print decorators + } + + // TODO: print typescript accessibility token + // TODO: print declare token + + if node.is_static() { + parts.push(ss!("static ")); + } + + if node.is_override() { + parts.push(ss!("override ")); + } + + if node.is_readonly() { + parts.push(ss!("readonly ")); + } + + // TODO: print abstract token + + if matches!(node, ClassMemberish::AccessorProperty(_)) { + parts.push(ss!("readonly ")); + } + + parts.push(node.format_key(p)); + + // TODO: print optional token + // TODO: print definite token + // TODO: print type annotation + + let right_expr = node.right_expr(); + let node = match node { + ClassMemberish::PropertyDefinition(v) => AssignmentLikeNode::PropertyDefinition(v), + ClassMemberish::AccessorProperty(v) => AssignmentLikeNode::AccessorProperty(v), + }; + let mut result = + assignment::print_assignment(p, node, Doc::Array(parts), Doc::Str(" ="), right_expr); + + if p.options.semi { + let mut parts = p.vec(); + parts.push(result); + result = Doc::Array(parts); + } + result +} diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index 5be7e96ad..677df4f6f 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -1874,7 +1874,7 @@ impl<'a> Format<'a> for Class<'a> { impl<'a> Format<'a> for ClassBody<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - class::print_class_body(p, self) + wrap!(p, self, ClassBody, { class::print_class_body(p, self) }) } } @@ -2034,19 +2034,21 @@ impl<'a> Format<'a> for StaticBlock<'a> { impl<'a> Format<'a> for MethodDefinition<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - function::print_method(p, self) + wrap!(p, self, MethodDefinition, { function::print_method(p, self) }) } } impl<'a> Format<'a> for PropertyDefinition<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + wrap!(p, self, PropertyDefinition, { + class::print_class_property(p, &class::ClassMemberish::PropertyDefinition(self)) + }) } } impl<'a> Format<'a> for AccessorProperty<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - line!() + class::print_class_property(p, &class::ClassMemberish::AccessorProperty(self)) } } diff --git a/tasks/prettier_conformance/prettier.snap.md b/tasks/prettier_conformance/prettier.snap.md index ba3b2b4dd..5501afacd 100644 --- a/tasks/prettier_conformance/prettier.snap.md +++ b/tasks/prettier_conformance/prettier.snap.md @@ -1,4 +1,4 @@ -Compatibility: 180/591 (30.46%) +Compatibility: 182/591 (30.80%) # Failed @@ -128,7 +128,6 @@ Compatibility: 180/591 (30.46%) * class-static-block/class-static-block.js ### classes -* classes/asi.js * classes/assignment.js * classes/class-fields-features.js * classes/empty.js @@ -230,9 +229,6 @@ Compatibility: 180/591 (30.46%) ### comments-pipeline-own-line * comments-pipeline-own-line/pipeline_own_line.js -### computed-props -* computed-props/classes.js - ### conditional * conditional/comments.js * conditional/new-ternary-examples.js