diff --git a/crates/oxc_formatter/DIVERGENCES.md b/crates/oxc_formatter/DIVERGENCES.md index 9066f3fdbe..b7be4f3510 100644 --- a/crates/oxc_formatter/DIVERGENCES.md +++ b/crates/oxc_formatter/DIVERGENCES.md @@ -1167,3 +1167,36 @@ x = An own-line comment before a member's `.` stays before it, for `.b` and `.#b` alike. Prettier moves it before the object for `.b`, and after the `.` for `.#b`; both cross a token. + +## computed-member-object-paren-break + +- Why: uniform-rule (same construct, same output: `(a + b).c` and `(await a)[c]`) +- Pin: `tests/fixtures/js/parenthesis/computed-member-object-paren-break.js` +- Conformance: `js/ternaries/indent-after-paren.js` + +```js +// input (printWidth: 80) +(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)[c]; +(someCondition ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeee)[c]; + +// ours: the same as `.c` +( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +)[c]; +(someCondition + ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee + : someVeryLongAlternateValueeeeeeeeeee +)[c]; + +// prettier +(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)[c]; +(someCondition + ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee + : someVeryLongAlternateValueeeeeeeeeee)[c]; +``` + +A binary-like or conditional object of a member breaks its parens the same way for `.c`, `[c]` and `?.[c]`. +Prettier does so for `.c` only, while an `await` / `as` object breaks them for `[c]` too. +The computed property (`a[b + c]`) keeps its own layout. diff --git a/crates/oxc_formatter/src/ast_nodes/impls/ast_nodes.rs b/crates/oxc_formatter/src/ast_nodes/impls/ast_nodes.rs index c45a0d3081..64ab8f6169 100644 --- a/crates/oxc_formatter/src/ast_nodes/impls/ast_nodes.rs +++ b/crates/oxc_formatter/src/ast_nodes/impls/ast_nodes.rs @@ -66,11 +66,6 @@ impl<'parent, 'a> AstNodes<'parent, 'a> { ) } - /// Check if this is a non-computed member expression: `a.b` or `a.#b` - pub fn is_non_computed_member_expression(&self) -> bool { - matches!(self, AstNodes::StaticMemberExpression(_) | AstNodes::PrivateFieldExpression(_)) - } - /// Check if this is a binary-like expression: `a || b`, `a + b`, or `#x in b` pub fn is_binary_like(&self) -> bool { matches!( diff --git a/crates/oxc_formatter/src/print/binary_like_expression.rs b/crates/oxc_formatter/src/print/binary_like_expression.rs index e76f1ac62b..f5b3b1eda0 100644 --- a/crates/oxc_formatter/src/print/binary_like_expression.rs +++ b/crates/oxc_formatter/src/print/binary_like_expression.rs @@ -224,7 +224,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLikeExpression<'a, '_> { } // Add a group with a soft block indent in cases where it is necessary to parenthesize the binary expression. - // For example, `(a+b)(call)`, `!(a + b)`, `(a + b).test`. + // For example, `(a+b)(call)`, `!(a + b)`, `(a + b).test`, `(a + b)[test]`. let is_inside_parenthesis = match parent { // The unary's own comment parens already provide the group and indent AstNodes::UnaryExpression(unary) if unary_argument_takes_comment_parens(unary, f) => { @@ -232,7 +232,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLikeExpression<'a, '_> { } AstNodes::UnaryExpression(_) => true, _ => { - parent.is_non_computed_member_expression() + parent.is_member_object_span(self.span()) || parent.is_call_like_callee_span(self.span()) } }; diff --git a/crates/oxc_formatter/src/utils/conditional.rs b/crates/oxc_formatter/src/utils/conditional.rs index 4f54b7bc77..9d9a987d39 100644 --- a/crates/oxc_formatter/src/utils/conditional.rs +++ b/crates/oxc_formatter/src/utils/conditional.rs @@ -36,11 +36,6 @@ impl<'a> ConditionalLike<'a, '_> { ConditionalLike::TSConditionalType(ty) => ty.parent(), } } - - #[inline] - fn is_conditional_expression(&self) -> bool { - matches!(self, ConditionalLike::ConditionalExpression(_)) - } } /// Layout information for a conditional expression to determine formatting strategy @@ -339,14 +334,6 @@ impl<'a> FormatConditionalLike<'a, '_> { } } - /// Returns `true` if this is the root conditional expression and the parent is a non-computed member: `.b` or `.#b` - #[inline] - fn is_parent_static_member_expression(&self, layout: ConditionalLayout) -> bool { - layout.is_root() - && self.is_conditional_expression() - && self.parent().is_non_computed_member_expression() - } - /// Formats the test part of the conditional fn format_test<'f>(&self, f: &mut JsFormatter<'f, 'a>, layout: ConditionalLayout) { let format_inner = format_with(|f| { @@ -550,16 +537,16 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatConditionalLike<'a, '_> { format_tail_with_indent.fmt(f); - // Add a soft line break in front of the closing `)` in case the parent is a static member expression + // Add a soft line break in front of the closing `)` in case it is the object of a member expression // ```text // (veryLongCondition // ? a // : b // <- enforce line break here if the conditional breaks - // ).more + // ).more // or `)[more]` // ``` if !should_extra_indent && !is_jsx_chain - && self.is_parent_static_member_expression(layout) + && self.parent().is_member_object_span(self.span()) { write!(f, [soft_line_break()]); } diff --git a/crates/oxc_formatter/tests/fixtures/js/parenthesis/computed-member-object-paren-break.js b/crates/oxc_formatter/tests/fixtures/js/parenthesis/computed-member-object-paren-break.js new file mode 100644 index 0000000000..7a8c257bd3 --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/js/parenthesis/computed-member-object-paren-break.js @@ -0,0 +1,9 @@ +// DIVERGENCES.md#computed-member-object-paren-break +(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb).c; +(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)[c]; +(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)?.[c]; +(someCondition ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeee).c; +(someCondition ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeee)[c]; + +// The property keeps its own layout +xxxxxxxxxx[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb]; diff --git a/crates/oxc_formatter/tests/fixtures/js/parenthesis/computed-member-object-paren-break.js.snap b/crates/oxc_formatter/tests/fixtures/js/parenthesis/computed-member-object-paren-break.js.snap new file mode 100644 index 0000000000..e78e50480b --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/js/parenthesis/computed-member-object-paren-break.js.snap @@ -0,0 +1,78 @@ +--- +source: crates/oxc_formatter/tests/fixtures/mod.rs +--- +==================== Input ==================== +// DIVERGENCES.md#computed-member-object-paren-break +(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb).c; +(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)[c]; +(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)?.[c]; +(someCondition ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeee).c; +(someCondition ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeee)[c]; + +// The property keeps its own layout +xxxxxxxxxx[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb]; + +==================== Output ==================== +------------------ +{ printWidth: 80 } +------------------ +// DIVERGENCES.md#computed-member-object-paren-break +( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +).c; +( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +)[c]; +( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +)?.[c]; +(someCondition + ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee + : someVeryLongAlternateValueeeeeeeeeee +).c; +(someCondition + ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee + : someVeryLongAlternateValueeeeeeeeeee +)[c]; + +// The property keeps its own layout +xxxxxxxxxx[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +]; + +------------------- +{ printWidth: 100 } +------------------- +// DIVERGENCES.md#computed-member-object-paren-break +( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +).c; +( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +)[c]; +( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +)?.[c]; +(someCondition + ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee + : someVeryLongAlternateValueeeeeeeeeee +).c; +(someCondition + ? someVeryLongConsequentValueeeeeeeeeeeeeeeeeeee + : someVeryLongAlternateValueeeeeeeeeee +)[c]; + +// The property keeps its own layout +xxxxxxxxxx[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +]; + +===================== End ===================== diff --git a/crates/oxc_formatter/tests/snapshots/conformance__prettier-js.snap b/crates/oxc_formatter/tests/snapshots/conformance__prettier-js.snap index f0905156c7..f1343f85d7 100644 --- a/crates/oxc_formatter/tests/snapshots/conformance__prettier-js.snap +++ b/crates/oxc_formatter/tests/snapshots/conformance__prettier-js.snap @@ -2,7 +2,7 @@ source: crates/oxc_formatter/tests/conformance.rs expression: report --- -js compatibility: 1033/1123 (91.99%), 23 files skipped +js compatibility: 1029/1123 (91.63%), 23 files skipped # Failed @@ -55,6 +55,7 @@ js compatibility: 1033/1123 (91.99%), 23 files skipped | js/switch/comments.js | 💥 | 94.74% | | js/template-literals/expression-break.js | 💥 | 80.00% | | js/template-literals/expressions.js | 💥 | 97.64% | +| js/ternaries/indent-after-paren.js | 💥💥💥💥 | 99.14% | | js/test-declarations/jest-each.js | 💥💥 | 95.71% | | js/trailing-comma/trailing_whitespace.js | 💥💥💥 | 96.55% | | js/try/try.js | 💥 | 44.44% | diff --git a/tasks/track_memory_allocations/allocs_formatter.yaml b/tasks/track_memory_allocations/allocs_formatter.yaml index 61ba7b9b27..0a4183c828 100644 --- a/tasks/track_memory_allocations/allocs_formatter.yaml +++ b/tasks/track_memory_allocations/allocs_formatter.yaml @@ -44,9 +44,9 @@ pdf.mjs: antd.js: file size: 6686316 # 6.69 MB - sys allocs: 74785 + sys allocs: 74784 sys reallocs: 351 - sys deallocs: 74784 + sys deallocs: 74783 sys alloc bytes: 23137418 # 23.14 MB sys peak growth: 13710144 # 13.71 MB arena allocs: 120676