Skip to content

Commit 87a35d7

Browse files
committed
feat(formatter): complete printing JSX-related nodes
1 parent 43b697e commit 87a35d7

File tree

19 files changed

+2383
-367
lines changed

19 files changed

+2383
-367
lines changed

crates/oxc_formatter/examples/formatter.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use std::{fs, path::Path};
1414

1515
use oxc_allocator::Allocator;
16-
use oxc_formatter::{FormatOptions, Formatter};
16+
use oxc_formatter::{BracketSameLine, FormatOptions, Formatter};
1717
use oxc_parser::{ParseOptions, Parser};
1818
use oxc_span::SourceType;
1919
use pico_args::Arguments;
@@ -46,7 +46,8 @@ fn main() -> Result<(), String> {
4646
}
4747

4848
// Format the parsed code
49-
let options = FormatOptions::default();
49+
let options =
50+
FormatOptions { bracket_same_line: BracketSameLine::from(true), ..Default::default() };
5051
let code = Formatter::new(&allocator, options).build(&ret.program);
5152

5253
println!("{code}");

crates/oxc_formatter/src/generated/format.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,9 +1259,14 @@ impl<'a> Format<'a> for AstNode<'a, RegExpLiteral<'a>> {
12591259

12601260
impl<'a> Format<'a> for AstNode<'a, JSXElement<'a>> {
12611261
fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
1262-
self.format_leading_comments(f)?;
1262+
let needs_parentheses = self.needs_parentheses(f);
1263+
if needs_parentheses {
1264+
"(".fmt(f)?;
1265+
}
12631266
let result = self.write(f);
1264-
self.format_trailing_comments(f)?;
1267+
if needs_parentheses {
1268+
")".fmt(f)?;
1269+
}
12651270
result
12661271
}
12671272
}
@@ -1286,9 +1291,14 @@ impl<'a> Format<'a> for AstNode<'a, JSXClosingElement<'a>> {
12861291

12871292
impl<'a> Format<'a> for AstNode<'a, JSXFragment<'a>> {
12881293
fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
1289-
self.format_leading_comments(f)?;
1294+
let needs_parentheses = self.needs_parentheses(f);
1295+
if needs_parentheses {
1296+
"(".fmt(f)?;
1297+
}
12901298
let result = self.write(f);
1291-
self.format_trailing_comments(f)?;
1299+
if needs_parentheses {
1300+
")".fmt(f)?;
1301+
}
12921302
result
12931303
}
12941304
}

crates/oxc_formatter/src/parentheses/expression.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ impl<'a> NeedsParentheses<'a> for AstNode<'a, Expression<'a>> {
5151
AstNodes::UpdateExpression(it) => it.needs_parentheses(f),
5252
AstNodes::YieldExpression(it) => it.needs_parentheses(f),
5353
AstNodes::PrivateInExpression(it) => it.needs_parentheses(f),
54-
// AstNodes::JSXElement(it) => it.needs_parentheses(f),
55-
// AstNodes::JSXFragment(it) => it.needs_parentheses(f),
54+
AstNodes::JSXElement(it) => it.needs_parentheses(f),
55+
AstNodes::JSXFragment(it) => it.needs_parentheses(f),
5656
AstNodes::TSAsExpression(it) => it.needs_parentheses(f),
5757
AstNodes::TSSatisfiesExpression(it) => it.needs_parentheses(f),
5858
AstNodes::TSTypeAssertion(it) => it.needs_parentheses(f),
@@ -313,6 +313,7 @@ impl<'a> NeedsParentheses<'a> for AstNode<'a, ConditionalExpression<'a>> {
313313
| AstNodes::TSAsExpression(_)
314314
| AstNodes::TSSatisfiesExpression(_)
315315
| AstNodes::SpreadElement(_)
316+
| AstNodes::JSXSpreadAttribute(_)
316317
| AstNodes::LogicalExpression(_)
317318
| AstNodes::BinaryExpression(_)
318319
) {
@@ -549,10 +550,12 @@ fn binary_like_needs_parens(binary_like: BinaryLikeExpression<'_, '_>) -> bool {
549550
| AstNodes::AwaitExpression(_)
550551
| AstNodes::TSNonNullExpression(_)
551552
| AstNodes::SpreadElement(_)
553+
| AstNodes::JSXSpreadAttribute(_)
552554
| AstNodes::CallExpression(_)
553555
| AstNodes::NewExpression(_)
554556
| AstNodes::StaticMemberExpression(_)
555557
| AstNodes::TaggedTemplateExpression(_) => return true,
558+
556559
AstNodes::Class(class) => {
557560
return class.super_class.as_ref().is_some_and(|super_class| {
558561
super_class.span().contains_inclusive(binary_like.span())
@@ -793,3 +796,43 @@ fn is_class_extends(span: Span, parent: &AstNodes<'_>) -> bool {
793796
}
794797
false
795798
}
799+
800+
fn jsx_element_or_fragment_needs_paren(span: Span, parent: &AstNodes<'_>) -> bool {
801+
if is_class_extends(span, parent) {
802+
return true;
803+
}
804+
805+
match parent {
806+
AstNodes::BinaryExpression(binary) => {
807+
let is_left = binary.left.span() == span;
808+
binary.operator == BinaryOperator::LessThan && is_left
809+
}
810+
AstNodes::TSAsExpression(_)
811+
| AstNodes::TSSatisfiesExpression(_)
812+
| AstNodes::AwaitExpression(_)
813+
| AstNodes::StaticMemberExpression(_)
814+
| AstNodes::ComputedMemberExpression(_)
815+
| AstNodes::SequenceExpression(_)
816+
| AstNodes::UnaryExpression(_)
817+
| AstNodes::TSNonNullExpression(_)
818+
| AstNodes::SpreadElement(_)
819+
| AstNodes::CallExpression(_)
820+
| AstNodes::NewExpression(_)
821+
| AstNodes::TaggedTemplateExpression(_)
822+
| AstNodes::JSXSpreadAttribute(_)
823+
| AstNodes::JSXSpreadChild(_) => true,
824+
_ => false,
825+
}
826+
}
827+
828+
impl NeedsParentheses<'_> for AstNode<'_, JSXElement<'_>> {
829+
fn needs_parentheses(&self, f: &Formatter<'_, '_>) -> bool {
830+
jsx_element_or_fragment_needs_paren(self.span, self.parent)
831+
}
832+
}
833+
834+
impl NeedsParentheses<'_> for AstNode<'_, JSXFragment<'_>> {
835+
fn needs_parentheses(&self, f: &Formatter<'_, '_>) -> bool {
836+
jsx_element_or_fragment_needs_paren(self.span, self.parent)
837+
}
838+
}

crates/oxc_formatter/src/utils/assignment_like.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -741,24 +741,28 @@ fn is_poorly_breakable_member_or_call_chain<'a>(
741741
// Keeping track of all call expressions in the chain to check them later
742742
let mut call_expressions = vec![];
743743

744-
let mut expression = expression;
744+
let mut expression = expression.as_ast_nodes();
745745

746746
loop {
747-
expression = match expression.as_ast_nodes() {
748-
AstNodes::TSNonNullExpression(assertion) => assertion.expression(),
747+
expression = match expression {
748+
AstNodes::TSNonNullExpression(assertion) => assertion.expression().as_ast_nodes(),
749749
AstNodes::CallExpression(call_expression) => {
750750
is_chain = true;
751751
let callee = &call_expression.callee();
752752
call_expressions.push(call_expression);
753-
callee
753+
callee.as_ast_nodes()
754754
}
755755
AstNodes::StaticMemberExpression(node) => {
756756
is_chain = true;
757-
node.object()
757+
node.object().as_ast_nodes()
758758
}
759759
AstNodes::ComputedMemberExpression(node) => {
760760
is_chain = true;
761-
node.object()
761+
node.object().as_ast_nodes()
762+
}
763+
AstNodes::ChainExpression(chain) => {
764+
is_chain = true;
765+
chain.expression().as_ast_nodes()
762766
}
763767
AstNodes::IdentifierReference(_) | AstNodes::ThisExpression(_) => {
764768
is_chain_head_simple = true;

0 commit comments

Comments
 (0)