feat(prettier) improve class printing (#1565)

This commit is contained in:
Cameron 2023-11-28 02:54:33 +00:00 committed by GitHub
parent 2bfd28e6f5
commit 522cf29489
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 177 additions and 19 deletions

View file

@ -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(|| "<anonymous>", |id| id.name.as_str())
)
.into(),
Self::ClassBody(_) => "ClassBody".into(),
Self::ClassHeritage(_) => "ClassHeritage".into(),
Self::StaticBlock(_) => "StaticBlock".into(),
Self::PropertyDefinition(_) => "PropertyDefinition".into(),

View file

@ -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<ClassMemberish<'a, 'b>> 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,
}
}

View file

@ -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(_)))

View file

@ -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<Decorator<'a>>> {
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
}

View file

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

View file

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