mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(formatter): print x.b and x.#b samely (#28193)
There is no reason to handle them separately. This change also fixes idempotency issue with comments.
This commit is contained in:
1 parent
9ddccb7105
commit
b8f4f34bba
27 files changed
+970
-209
No files matched your search
@@ -1079,3 +1079,91 @@ class A {
|
|||||||
A class member named `in` or `instanceof` would continue the previous property's value as a binary operator, so the `;` before it is kept.
|
A class member named `in` or `instanceof` would continue the previous property's value as a binary operator, so the `;` before it is kept.
|
||||||
Prettier checks only an identifier key, but also prints the string key `"in"` unquoted, producing a syntax error.
|
Prettier checks only an identifier key, but also prints the string key `"in"` unquoted, producing a syntax error.
|
||||||
We check a string key by its value, which also keeps the `;` before a key that stays quoted (TypeScript, `quoteProps: "preserve"`), where Prettier's output stays valid.
|
We check a string key by its value, which also keeps the `;` before a key that stays quoted (TypeScript, `quoteProps: "preserve"`), where Prettier's output stays valid.
|
||||||
|
|
||||||
|
## private-member-like-static
|
||||||
|
|
||||||
|
- Why: uniform-rule (same construct, same output: `a.b`)
|
||||||
|
- Pin: `tests/fixtures/js/private-fields/private-member-like-static.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
// input (printWidth: 80)
|
||||||
|
foo(someIdentifierName.#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking);
|
||||||
|
this.#Factory.someMethodName(argumentOne).anotherMethodName(argumentTwo).third();
|
||||||
|
const a = obj.#decorator(someArgument)((aaaaaaaaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccc) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
@A.#decorator a() {}
|
||||||
|
|
||||||
|
// ours: the same as `.b`
|
||||||
|
foo(
|
||||||
|
someIdentifierName.#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking,
|
||||||
|
);
|
||||||
|
this.#Factory.someMethodName(argumentOne)
|
||||||
|
.anotherMethodName(argumentTwo)
|
||||||
|
.third();
|
||||||
|
const a = obj.#decorator(someArgument)((
|
||||||
|
aaaaaaaaaaaa,
|
||||||
|
bbbbbbbbbbbbbbbb,
|
||||||
|
cccccccccccccccc,
|
||||||
|
) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
@A.#decorator a() {}
|
||||||
|
|
||||||
|
// prettier
|
||||||
|
foo(
|
||||||
|
someIdentifierName
|
||||||
|
.#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking,
|
||||||
|
);
|
||||||
|
this.#Factory
|
||||||
|
.someMethodName(argumentOne)
|
||||||
|
.anotherMethodName(argumentTwo)
|
||||||
|
.third();
|
||||||
|
const a = obj.#decorator(someArgument)(
|
||||||
|
(aaaaaaaaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccc) => {
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
@(A.#decorator) a() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Both are a non-computed member access, so `.#b` follows every rule of `.b`.
|
||||||
|
Prettier prints `.b` like ours in all four, only `.#b` differently.
|
||||||
|
`@A.#decorator` is valid without parens (`DecoratorMemberExpression` accepts a `PrivateIdentifier`).
|
||||||
|
|
||||||
|
## member-property-own-line-comment
|
||||||
|
|
||||||
|
- Why: invariant
|
||||||
|
- Pin: `tests/fixtures/js/comments/member-property-own-line-comment.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
// input
|
||||||
|
x = a
|
||||||
|
// comment
|
||||||
|
.b;
|
||||||
|
x = a
|
||||||
|
// comment
|
||||||
|
.#b;
|
||||||
|
|
||||||
|
// ours
|
||||||
|
x =
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.b;
|
||||||
|
x =
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.#b;
|
||||||
|
|
||||||
|
// prettier
|
||||||
|
x =
|
||||||
|
// comment
|
||||||
|
a.b;
|
||||||
|
x =
|
||||||
|
a
|
||||||
|
.// comment
|
||||||
|
#b;
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -56,6 +56,21 @@ impl<'parent, 'a> AstNodes<'parent, 'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if this is a member expression: `a.b`, `a[b]`, or `a.#b`
|
||||||
|
pub fn is_member_expression(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
AstNodes::StaticMemberExpression(_)
|
||||||
|
| AstNodes::ComputedMemberExpression(_)
|
||||||
|
| AstNodes::PrivateFieldExpression(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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`
|
/// Check if this is a binary-like expression: `a || b`, `a + b`, or `#x in b`
|
||||||
pub fn is_binary_like(&self) -> bool {
|
pub fn is_binary_like(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
|
|||||||
@@ -1196,9 +1196,6 @@ fn jsx_element_or_fragment_needs_paren(span: Span, parent: &AstNodes<'_, '_>) ->
|
|||||||
AstNodes::TSAsExpression(_)
|
AstNodes::TSAsExpression(_)
|
||||||
| AstNodes::TSSatisfiesExpression(_)
|
| AstNodes::TSSatisfiesExpression(_)
|
||||||
| AstNodes::AwaitExpression(_)
|
| AstNodes::AwaitExpression(_)
|
||||||
| AstNodes::StaticMemberExpression(_)
|
|
||||||
| AstNodes::ComputedMemberExpression(_)
|
|
||||||
| AstNodes::PrivateFieldExpression(_)
|
|
||||||
| AstNodes::SequenceExpression(_)
|
| AstNodes::SequenceExpression(_)
|
||||||
| AstNodes::UnaryExpression(_)
|
| AstNodes::UnaryExpression(_)
|
||||||
| AstNodes::TSNonNullExpression(_)
|
| AstNodes::TSNonNullExpression(_)
|
||||||
@@ -1206,8 +1203,7 @@ fn jsx_element_or_fragment_needs_paren(span: Span, parent: &AstNodes<'_, '_>) ->
|
|||||||
| AstNodes::TaggedTemplateExpression(_)
|
| AstNodes::TaggedTemplateExpression(_)
|
||||||
| AstNodes::JSXSpreadAttribute(_)
|
| AstNodes::JSXSpreadAttribute(_)
|
||||||
| AstNodes::JSXSpreadChild(_) => true,
|
| AstNodes::JSXSpreadChild(_) => true,
|
||||||
_ if parent.is_call_like_callee_span(span) => true,
|
_ => parent.is_member_expression() || parent.is_call_like_callee_span(span),
|
||||||
_ => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,15 +132,10 @@ fn format_as_or_satisfies_expression<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_callee_or_object_context(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
fn is_callee_or_object_context(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||||
match parent {
|
parent.is_member_object_span(span)
|
||||||
// Static member
|
|
||||||
AstNodes::StaticMemberExpression(_) => true,
|
|
||||||
AstNodes::ComputedMemberExpression(member) => member.object.span() == span,
|
|
||||||
// Or CallExpression callee (Not NewExpression, to align with Prettier)
|
// Or CallExpression callee (Not NewExpression, to align with Prettier)
|
||||||
// https://github.com/prettier/prettier/blob/fdfa6701767f5140a85902ecc9fb6444f5b4e3f8/src/language-js/print/cast-expression.js#L28-L33
|
// https://github.com/prettier/prettier/blob/fdfa6701767f5140a85902ecc9fb6444f5b4e3f8/src/language-js/print/cast-expression.js#L28-L33
|
||||||
// NOTE: We may revert this if resolved: https://github.com/prettier/prettier/issues/18406
|
// NOTE: We may revert this if resolved: https://github.com/prettier/prettier/issues/18406
|
||||||
// _ => parent.is_call_like_callee_span(span),
|
// || parent.is_call_like_callee_span(span)
|
||||||
AstNodes::CallExpression(call) => call.callee.span() == span,
|
|| matches!(parent, AstNodes::CallExpression(call) if call.callee.span() == span)
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -230,8 +230,11 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLikeExpression<'a, '_> {
|
|||||||
AstNodes::UnaryExpression(unary) if unary_argument_takes_comment_parens(unary, f) => {
|
AstNodes::UnaryExpression(unary) if unary_argument_takes_comment_parens(unary, f) => {
|
||||||
return format_flattened_logical_expression(*self, false, f);
|
return format_flattened_logical_expression(*self, false, f);
|
||||||
}
|
}
|
||||||
AstNodes::StaticMemberExpression(_) | AstNodes::UnaryExpression(_) => true,
|
AstNodes::UnaryExpression(_) => true,
|
||||||
_ => parent.is_call_like_callee_span(self.span()),
|
_ => {
|
||||||
|
parent.is_non_computed_member_expression()
|
||||||
|
|| parent.is_call_like_callee_span(self.span())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_inside_parenthesis {
|
if is_inside_parenthesis {
|
||||||
|
|||||||
@@ -1122,12 +1122,16 @@ fn is_decorated_function(argument: &AstNode<'_, '_, Argument<'_>>) -> bool {
|
|||||||
|
|
||||||
// Check if the decorator (callee.callee) is a simple identifier or member expression
|
// Check if the decorator (callee.callee) is a simple identifier or member expression
|
||||||
let decorator = &callee_call.callee;
|
let decorator = &callee_call.callee;
|
||||||
let is_valid_decorator = matches!(decorator, Expression::Identifier(_))
|
let is_valid_decorator = match decorator {
|
||||||
|| matches!(
|
Expression::Identifier(_) => true,
|
||||||
decorator,
|
Expression::StaticMemberExpression(member) => {
|
||||||
Expression::StaticMemberExpression(member)
|
matches!(&member.object, Expression::Identifier(_))
|
||||||
if matches!(&member.object, Expression::Identifier(_))
|
}
|
||||||
);
|
Expression::PrivateFieldExpression(member) => {
|
||||||
|
matches!(&member.object, Expression::Identifier(_))
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
if !is_valid_decorator {
|
if !is_valid_decorator {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -42,10 +42,8 @@ impl<'a> FormatWrite<'a> for AstNode<'_, 'a, CallExpression<'a>> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(
|
if callee.is_member_expression()
|
||||||
callee.as_ref(),
|
&& !is_verbatim_multiline_template_sole_arg(arguments.as_slice(), Some(self), f)
|
||||||
Expression::StaticMemberExpression(_) | Expression::ComputedMemberExpression(_)
|
|
||||||
) && !is_verbatim_multiline_template_sole_arg(arguments.as_slice(), Some(self), f)
|
|
||||||
&& !is_simple_module_import(&self.arguments(), f.comments())
|
&& !is_simple_module_import(&self.arguments(), f.comments())
|
||||||
&& !is_test_call_expression(self, f.comments())
|
&& !is_test_call_expression(self, f.comments())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, Decora
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_identifier_or_static_member_only(callee: &Expression) -> bool {
|
fn is_identifier_or_non_computed_member_only(callee: &Expression) -> bool {
|
||||||
let mut expr = callee;
|
let mut expr = callee;
|
||||||
loop {
|
loop {
|
||||||
match expr {
|
match expr {
|
||||||
@@ -60,6 +60,9 @@ fn is_identifier_or_static_member_only(callee: &Expression) -> bool {
|
|||||||
Expression::StaticMemberExpression(static_member) => {
|
Expression::StaticMemberExpression(static_member) => {
|
||||||
expr = &static_member.object;
|
expr = &static_member.object;
|
||||||
}
|
}
|
||||||
|
Expression::PrivateFieldExpression(private_field) => {
|
||||||
|
expr = &private_field.object;
|
||||||
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,16 +76,13 @@ impl<'a> FormatWrite<'a> for AstNode<'_, 'a, Decorator<'a>> {
|
|||||||
|
|
||||||
// Determine if parentheses are required around decorator expressions
|
// Determine if parentheses are required around decorator expressions
|
||||||
let needs_parentheses = match &self.expression {
|
let needs_parentheses = match &self.expression {
|
||||||
// Identifiers: `@decorator` needs no parens
|
|
||||||
Expression::Identifier(_) => false,
|
|
||||||
// Call expressions: `@obj.method()` needs no parens, `@(complex().method)()` needs parens
|
// Call expressions: `@obj.method()` needs no parens, `@(complex().method)()` needs parens
|
||||||
Expression::CallExpression(call) => !is_identifier_or_static_member_only(&call.callee),
|
Expression::CallExpression(call) => {
|
||||||
// Static member expressions: `@obj.prop` needs no parens, `@(complex[key])` needs parens
|
!is_identifier_or_non_computed_member_only(&call.callee)
|
||||||
Expression::StaticMemberExpression(static_member) => {
|
|
||||||
!is_identifier_or_static_member_only(&static_member.object)
|
|
||||||
}
|
}
|
||||||
// All other expressions need parentheses: `@(a + b)`, `@(condition ? a : b)`
|
// `@decorator`, `@obj.prop` and `@obj.#prop` need no parens,
|
||||||
_ => true,
|
// all other expressions need parens: `@(complex[key])`, `@(a + b)`
|
||||||
|
expression => !is_identifier_or_non_computed_member_only(expression),
|
||||||
};
|
};
|
||||||
|
|
||||||
if needs_parentheses {
|
if needs_parentheses {
|
||||||
|
|||||||
@@ -95,25 +95,11 @@ impl<'a> AnyJsxTagWithChildren<'a, '_> {
|
|||||||
| AstNodes::JSXExpressionContainer(_)
|
| AstNodes::JSXExpressionContainer(_)
|
||||||
| AstNodes::ConditionalExpression(_)
|
| AstNodes::ConditionalExpression(_)
|
||||||
| AstNodes::ExpressionStatement(_) => WrapState::NoWrap,
|
| AstNodes::ExpressionStatement(_) => WrapState::NoWrap,
|
||||||
AstNodes::StaticMemberExpression(member) => {
|
|
||||||
if member.optional {
|
|
||||||
WrapState::NoWrap
|
|
||||||
} else {
|
|
||||||
WrapState::WrapOnBreak
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// It is a argument of a call expression
|
// It is a argument of a call expression
|
||||||
AstNodes::CallExpression(call) if call.is_argument_span(self.span()) => {
|
AstNodes::CallExpression(call) if call.is_argument_span(self.span()) => {
|
||||||
WrapState::NoWrap
|
WrapState::NoWrap
|
||||||
}
|
}
|
||||||
AstNodes::NewExpression(new) if new.is_argument_span(self.span()) => WrapState::NoWrap,
|
AstNodes::NewExpression(new) if new.is_argument_span(self.span()) => WrapState::NoWrap,
|
||||||
AstNodes::ComputedMemberExpression(member) => {
|
|
||||||
if member.optional {
|
|
||||||
WrapState::NoWrap
|
|
||||||
} else {
|
|
||||||
WrapState::WrapOnBreak
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Concise arrow body: `() => <div></div>`
|
// Concise arrow body: `() => <div></div>`
|
||||||
#[expect(clippy::match_same_arms)]
|
#[expect(clippy::match_same_arms)]
|
||||||
AstNodes::ArrowFunctionExpression(_) => WrapState::WrapOnBreak,
|
AstNodes::ArrowFunctionExpression(_) => WrapState::WrapOnBreak,
|
||||||
|
|||||||
@@ -26,48 +26,66 @@ impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ComputedMemberExpression<'a>> {
|
|||||||
|
|
||||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, StaticMemberExpression<'a>> {
|
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, StaticMemberExpression<'a>> {
|
||||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||||
// Before the recording: printing the object consumes its cast comment
|
write_non_computed_member(self.object(), self.optional(), self.property(), f);
|
||||||
let object_is_cast_target = is_cast_target(self.object.span(), f);
|
}
|
||||||
let is_member_chain = {
|
}
|
||||||
let start = f.elements().len();
|
|
||||||
write!(f, [self.object()]);
|
|
||||||
f.elements()[start..].has_label(LabelId::of(JsLabels::MemberChain))
|
|
||||||
};
|
|
||||||
|
|
||||||
match layout(self, is_member_chain, object_is_cast_target, f) {
|
/// `a.#b` is laid out the same as `a.b`.
|
||||||
StaticMemberLayout::NoBreak => {
|
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, PrivateFieldExpression<'a>> {
|
||||||
let format_no_break =
|
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||||
format_with(|f| write!(f, [operator_token(self.optional()), self.property()]));
|
write_non_computed_member(self.object(), self.optional(), self.field(), f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if is_member_chain {
|
/// Writes `a.b` or `a.#b`.
|
||||||
write!(f, [labelled(LabelId::of(JsLabels::MemberChain), &format_no_break)]);
|
fn write_non_computed_member<'b, 'a, P: GetSpan>(
|
||||||
} else {
|
object: AstNode<'b, 'a, Expression<'a>>,
|
||||||
write!(f, [format_no_break]);
|
optional: bool,
|
||||||
}
|
property: AstNode<'b, 'a, P>,
|
||||||
}
|
f: &mut JsFormatter<'_, 'a>,
|
||||||
StaticMemberLayout::BreakAfterObject => {
|
) where
|
||||||
write!(
|
AstNode<'b, 'a, P>: Format<'a, JsFormatContext<'a>>,
|
||||||
f,
|
{
|
||||||
[group(&indent(&format_args!(
|
// Before the recording: printing the object consumes its cast comment
|
||||||
soft_line_break(),
|
let object_is_cast_target = is_cast_target(object.span(), f);
|
||||||
&format_with(|f| {
|
let is_member_chain = {
|
||||||
if f.comments().has_leading_own_line_comment(self.property.span.start) {
|
let start = f.elements().len();
|
||||||
let comments = f
|
write!(f, [object]);
|
||||||
.context()
|
f.elements()[start..].has_label(LabelId::of(JsLabels::MemberChain))
|
||||||
.comments()
|
};
|
||||||
.comments_before(self.property.span.start);
|
let property_start = property.span().start;
|
||||||
write!(
|
// The member expression itself
|
||||||
f,
|
let member = property.parent();
|
||||||
[FormatLeadingComments::Comments(comments), soft_line_break()]
|
|
||||||
);
|
match layout(&object, property_start, member, is_member_chain, object_is_cast_target, f) {
|
||||||
}
|
StaticMemberLayout::NoBreak => {
|
||||||
}),
|
let format_no_break = format_with(|f| write!(f, [operator_token(optional), property]));
|
||||||
operator_token(self.optional()),
|
|
||||||
self.property(),
|
if is_member_chain {
|
||||||
)))]
|
write!(f, [labelled(LabelId::of(JsLabels::MemberChain), &format_no_break)]);
|
||||||
);
|
} else {
|
||||||
|
write!(f, [format_no_break]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
StaticMemberLayout::BreakAfterObject => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[group(&indent(&format_args!(
|
||||||
|
soft_line_break(),
|
||||||
|
&format_with(|f| {
|
||||||
|
if f.comments().has_leading_own_line_comment(property_start) {
|
||||||
|
let comments = f.context().comments().comments_before(property_start);
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[FormatLeadingComments::Comments(comments), soft_line_break()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
operator_token(optional),
|
||||||
|
property,
|
||||||
|
)))]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,23 +105,24 @@ fn operator_token(optional: bool) -> &'static str {
|
|||||||
/// `object_is_cast_target`:
|
/// `object_is_cast_target`:
|
||||||
/// a cast-target object is neither a call nor an identifier to the rules below (see `is_cast_target`).
|
/// a cast-target object is neither a call nor an identifier to the rules below (see `is_cast_target`).
|
||||||
fn layout<'a>(
|
fn layout<'a>(
|
||||||
node: &AstNode<'_, 'a, StaticMemberExpression<'a>>,
|
object: &Expression<'a>,
|
||||||
|
property_start: u32,
|
||||||
|
member: &AstNodes<'_, 'a>,
|
||||||
is_member_chain: bool,
|
is_member_chain: bool,
|
||||||
object_is_cast_target: bool,
|
object_is_cast_target: bool,
|
||||||
f: &JsFormatter<'_, 'a>,
|
f: &JsFormatter<'_, 'a>,
|
||||||
) -> StaticMemberLayout {
|
) -> StaticMemberLayout {
|
||||||
if f.comments().has_leading_own_line_comment(node.property.span.start) {
|
if f.comments().has_leading_own_line_comment(property_start) {
|
||||||
return StaticMemberLayout::BreakAfterObject;
|
return StaticMemberLayout::BreakAfterObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
// `a.b.c!` and `a.b?.c`
|
// `a.b.c!` and `a.b?.c`
|
||||||
// `TSNonNullExpression` is a wrapper node for `!`, and `ChainExpression` is a wrapper node for `?.`,
|
// `TSNonNullExpression` is a wrapper node for `!`, and `ChainExpression` is a wrapper node for `?.`,
|
||||||
// so we need to skip them to find the real parent node.
|
// so we need to skip them to find the real parent node.
|
||||||
let mut parent = node.parent();
|
let mut parent = member.parent();
|
||||||
while matches!(parent, AstNodes::TSNonNullExpression(_) | AstNodes::ChainExpression(_)) {
|
while matches!(parent, AstNodes::TSNonNullExpression(_) | AstNodes::ChainExpression(_)) {
|
||||||
parent = parent.parent();
|
parent = parent.parent();
|
||||||
}
|
}
|
||||||
let object = &node.object;
|
|
||||||
|
|
||||||
let is_nested = match parent {
|
let is_nested = match parent {
|
||||||
AstNodes::AssignmentExpression(_) | AstNodes::VariableDeclarator(_) => {
|
AstNodes::AssignmentExpression(_) | AstNodes::VariableDeclarator(_) => {
|
||||||
@@ -117,8 +136,7 @@ fn layout<'a>(
|
|||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
AstNodes::StaticMemberExpression(_) | AstNodes::ComputedMemberExpression(_) => true,
|
_ => parent.is_member_expression(),
|
||||||
_ => false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !is_nested && !object_is_cast_target && matches!(object, Expression::Identifier(_)) {
|
if !is_nested && !object_is_cast_target && matches!(object, Expression::Identifier(_)) {
|
||||||
@@ -126,18 +144,17 @@ fn layout<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut first_non_static_member_ancestor = parent;
|
let mut first_non_static_member_ancestor = parent;
|
||||||
while matches!(
|
while first_non_static_member_ancestor.is_member_expression()
|
||||||
first_non_static_member_ancestor,
|
|| matches!(
|
||||||
AstNodes::StaticMemberExpression(_)
|
first_non_static_member_ancestor,
|
||||||
| AstNodes::ComputedMemberExpression(_)
|
AstNodes::ChainExpression(_) | AstNodes::TSNonNullExpression(_)
|
||||||
| AstNodes::ChainExpression(_)
|
)
|
||||||
| AstNodes::TSNonNullExpression(_)
|
{
|
||||||
) {
|
|
||||||
first_non_static_member_ancestor = first_non_static_member_ancestor.parent();
|
first_non_static_member_ancestor = first_non_static_member_ancestor.parent();
|
||||||
}
|
}
|
||||||
|
|
||||||
match first_non_static_member_ancestor {
|
match first_non_static_member_ancestor {
|
||||||
AstNodes::NewExpression(expr) if expr.callee.span().contains_inclusive(node.span()) => {
|
AstNodes::NewExpression(expr) if expr.callee.span().contains_inclusive(member.span()) => {
|
||||||
StaticMemberLayout::NoBreak
|
StaticMemberLayout::NoBreak
|
||||||
}
|
}
|
||||||
AstNodes::AssignmentExpression(assignment) => {
|
AstNodes::AssignmentExpression(assignment) => {
|
||||||
@@ -150,9 +167,3 @@ fn layout<'a>(
|
|||||||
_ => StaticMemberLayout::BreakAfterObject,
|
_ => StaticMemberLayout::BreakAfterObject,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, PrivateFieldExpression<'a>> {
|
|
||||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
|
||||||
write!(f, [self.object(), self.optional().then_some("?"), ".", self.field()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -454,11 +454,8 @@ impl<'a> FormatWrite<'a> for AstNode<'_, 'a, AwaitExpression<'a>> {
|
|||||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||||
let format_inner = format_with(|f| write!(f, ["await", space(), self.argument()]));
|
let format_inner = format_with(|f| write!(f, ["await", space(), self.argument()]));
|
||||||
|
|
||||||
let is_callee_or_object = match self.parent() {
|
let is_callee_or_object = self.parent().is_member_object_span(self.span)
|
||||||
AstNodes::StaticMemberExpression(_) => true,
|
|| self.parent().is_call_like_callee_span(self.span);
|
||||||
AstNodes::ComputedMemberExpression(member) => member.object.span() == self.span(),
|
|
||||||
_ => self.parent().is_call_like_callee_span(self.span),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !is_callee_or_object {
|
if !is_callee_or_object {
|
||||||
write!(f, [format_inner]);
|
write!(f, [format_inner]);
|
||||||
|
|||||||
@@ -210,25 +210,18 @@ fn has_argument_leading_comments(argument: &AstNode<Expression>, f: &JsFormatter
|
|||||||
// This check is based on
|
// This check is based on
|
||||||
// <https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/language-js/comments/handle-comments.js#L335-L349>
|
// <https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/language-js/comments/handle-comments.js#L335-L349>
|
||||||
if let LeftSide::Expression(left_side) = left_side {
|
if let LeftSide::Expression(left_side) = left_side {
|
||||||
let has_leading_own_line_comment = match left_side {
|
let member = match left_side {
|
||||||
Expression::ChainExpression(chain) => {
|
Expression::ChainExpression(chain) => chain.expression.as_member_expression(),
|
||||||
if let ChainElement::StaticMemberExpression(member) = &chain.expression {
|
_ => left_side.as_member_expression(),
|
||||||
let comments = f
|
|
||||||
.comments()
|
|
||||||
.comments_in_range(member.object.span().end, member.property.span.end);
|
|
||||||
is_own_line_comment_or_multi_line_comment(comments)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::StaticMemberExpression(member) => {
|
|
||||||
let comments = f
|
|
||||||
.comments()
|
|
||||||
.comments_in_range(member.object.span().end, member.property.span.end);
|
|
||||||
is_own_line_comment_or_multi_line_comment(comments)
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
};
|
||||||
|
// `a.b` and `a.#b`, the comments after the object
|
||||||
|
let has_leading_own_line_comment = member.is_some_and(|member| {
|
||||||
|
!member.is_computed()
|
||||||
|
&& is_own_line_comment_or_multi_line_comment(
|
||||||
|
f.comments()
|
||||||
|
.comments_in_range(member.object().span().end, member.span().end),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
if has_leading_own_line_comment {
|
if has_leading_own_line_comment {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -270,21 +270,9 @@ impl<'a> FormatConditionalLike<'a, '_> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AstNodes::StaticMemberExpression(member) => {
|
member if member.is_member_object_span(expression_span) => {
|
||||||
if member.object.span() == expression_span {
|
expression_span = member.span();
|
||||||
expression_span = member.span();
|
parent = member.parent();
|
||||||
parent = member.parent();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AstNodes::ComputedMemberExpression(member) => {
|
|
||||||
if member.object.span() == expression_span {
|
|
||||||
expression_span = member.span();
|
|
||||||
parent = member.parent();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
AstNodes::CallExpression(call) => {
|
AstNodes::CallExpression(call) => {
|
||||||
if call.callee.span() == expression_span {
|
if call.callee.span() == expression_span {
|
||||||
@@ -351,12 +339,12 @@ impl<'a> FormatConditionalLike<'a, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if this is the root conditional expression and the parent is a [`StaticMemberExpression`].
|
/// Returns `true` if this is the root conditional expression and the parent is a non-computed member: `.b` or `.#b`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_parent_static_member_expression(&self, layout: ConditionalLayout) -> bool {
|
fn is_parent_static_member_expression(&self, layout: ConditionalLayout) -> bool {
|
||||||
layout.is_root()
|
layout.is_root()
|
||||||
&& self.is_conditional_expression()
|
&& self.is_conditional_expression()
|
||||||
&& matches!(self.parent(), AstNodes::StaticMemberExpression(_))
|
&& self.parent().is_non_computed_member_expression()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the test part of the conditional
|
/// Formats the test part of the conditional
|
||||||
|
|||||||
@@ -3,25 +3,27 @@ use std::ops::Deref;
|
|||||||
use crate::{
|
use crate::{
|
||||||
ast_nodes::{AstNode, AstNodes},
|
ast_nodes::{AstNode, AstNodes},
|
||||||
format_args,
|
format_args,
|
||||||
formatter::{JsFormatter, prelude::*, trivia::FormatLeadingComments},
|
formatter::{
|
||||||
|
JsFormatter,
|
||||||
|
prelude::*,
|
||||||
|
trivia::{FormatLeadingComments, format_trailing_comments},
|
||||||
|
},
|
||||||
utils::{call_expression::callee_opener, statement_body::write_comments_before_opener},
|
utils::{call_expression::callee_opener, statement_body::write_comments_before_opener},
|
||||||
write,
|
write,
|
||||||
};
|
};
|
||||||
use oxc_ast::ast::*;
|
use oxc_ast::ast::*;
|
||||||
use oxc_formatter_core::Format;
|
use oxc_formatter_core::Format;
|
||||||
use oxc_span::GetSpan;
|
use oxc_span::{GetSpan, Span};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum CallExpressionPosition {
|
pub enum CallExpressionPosition {
|
||||||
/// At the start of a call chain.
|
/// At the start of a call chain.
|
||||||
/// `of` in `of().test`
|
/// `of` in `of().test`
|
||||||
Start,
|
Start,
|
||||||
|
|
||||||
/// Somewhere in the middle.
|
/// Somewhere in the middle.
|
||||||
///
|
///
|
||||||
/// `b` in `a.b().c()`
|
/// `b` in `a.b().c()`
|
||||||
Middle,
|
Middle,
|
||||||
|
|
||||||
/// At the end of a call chain (root)
|
/// At the end of a call chain (root)
|
||||||
/// `c` in `a.b.c()`
|
/// `c` in `a.b.c()`
|
||||||
End,
|
End,
|
||||||
@@ -32,24 +34,22 @@ pub enum CallExpressionPosition {
|
|||||||
pub enum ChainMember<'a, 'b> {
|
pub enum ChainMember<'a, 'b> {
|
||||||
/// Holds onto a [oxc_ast::ast::StaticMemberExpression]
|
/// Holds onto a [oxc_ast::ast::StaticMemberExpression]
|
||||||
StaticMember(&'b AstNode<'b, 'a, StaticMemberExpression<'a>>),
|
StaticMember(&'b AstNode<'b, 'a, StaticMemberExpression<'a>>),
|
||||||
|
/// Holds onto a [oxc_ast::ast::PrivateFieldExpression], treated the same as `StaticMember`
|
||||||
|
PrivateField(&'b AstNode<'b, 'a, PrivateFieldExpression<'a>>),
|
||||||
/// Holds onto a [oxc_ast::ast::CallExpression]
|
/// Holds onto a [oxc_ast::ast::CallExpression]
|
||||||
CallExpression {
|
CallExpression {
|
||||||
expression: &'b AstNode<'b, 'a, CallExpression<'a>>,
|
expression: &'b AstNode<'b, 'a, CallExpression<'a>>,
|
||||||
position: CallExpressionPosition,
|
position: CallExpressionPosition,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Holds onto a [oxc_ast::ast::ComputedMemberExpression]
|
/// Holds onto a [oxc_ast::ast::ComputedMemberExpression]
|
||||||
ComputedMember(&'b AstNode<'b, 'a, ComputedMemberExpression<'a>>),
|
ComputedMember(&'b AstNode<'b, 'a, ComputedMemberExpression<'a>>),
|
||||||
|
|
||||||
TSNonNullExpression(&'b AstNode<'b, 'a, TSNonNullExpression<'a>>),
|
TSNonNullExpression(&'b AstNode<'b, 'a, TSNonNullExpression<'a>>),
|
||||||
|
/// Any other node that is not one of the above
|
||||||
/// Any other node that are not [oxc_ast::ast::CallExpression] or [oxc_ast::ast::StaticMemberExpression]
|
|
||||||
/// Are tracked using this variant
|
/// Are tracked using this variant
|
||||||
Node(&'b AstNode<'b, 'a, Expression<'a>>),
|
Node(&'b AstNode<'b, 'a, Expression<'a>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChainMember<'_, '_> {
|
impl<'a> ChainMember<'a, '_> {
|
||||||
pub(crate) const fn is_call_expression(&self) -> bool {
|
pub(crate) const fn is_call_expression(&self) -> bool {
|
||||||
matches!(self, Self::CallExpression { .. })
|
matches!(self, Self::CallExpression { .. })
|
||||||
}
|
}
|
||||||
@@ -58,9 +58,25 @@ impl ChainMember<'_, '_> {
|
|||||||
matches!(self, Self::ComputedMember { .. })
|
matches!(self, Self::ComputedMember { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The object, the property span and the property name of `a.b` or `a.#b`
|
||||||
|
pub fn as_non_computed_member(&self) -> Option<(&'a Expression<'a>, Span, &'a str)> {
|
||||||
|
match self {
|
||||||
|
Self::StaticMember(member) => {
|
||||||
|
let member = member.inner();
|
||||||
|
Some((&member.object, member.property.span, member.property.name.as_str()))
|
||||||
|
}
|
||||||
|
Self::PrivateField(member) => {
|
||||||
|
let member = member.inner();
|
||||||
|
Some((&member.object, member.field.span, member.field.name.as_str()))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn span(&self) -> oxc_span::Span {
|
pub fn span(&self) -> oxc_span::Span {
|
||||||
match self {
|
match self {
|
||||||
Self::StaticMember(member) => member.span(),
|
Self::StaticMember(member) => member.span(),
|
||||||
|
Self::PrivateField(member) => member.span(),
|
||||||
Self::TSNonNullExpression(e) => e.span(),
|
Self::TSNonNullExpression(e) => e.span(),
|
||||||
Self::CallExpression { expression, .. } => expression.span(),
|
Self::CallExpression { expression, .. } => expression.span(),
|
||||||
Self::ComputedMember(member) => member.span(),
|
Self::ComputedMember(member) => member.span(),
|
||||||
@@ -73,26 +89,10 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ChainMember<'a, '_> {
|
|||||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||||
match self {
|
match self {
|
||||||
Self::StaticMember(member) => {
|
Self::StaticMember(member) => {
|
||||||
write!(
|
write_member_lookup(member.optional(), member.property(), f);
|
||||||
f,
|
}
|
||||||
[
|
Self::PrivateField(member) => {
|
||||||
FormatLeadingComments::Comments(
|
write_member_lookup(member.optional(), member.field(), f);
|
||||||
f.context().comments().comments_before(member.property().span().start)
|
|
||||||
),
|
|
||||||
member.optional().then_some("?"),
|
|
||||||
".",
|
|
||||||
member.property()
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
// The callee of `A.b(c)` / `A.b<T>(c)`: its comments keep their side of the opener
|
|
||||||
if let AstNodes::CallExpression(call) = member.parent()
|
|
||||||
&& !call.optional
|
|
||||||
{
|
|
||||||
write_comments_before_opener(member.span().end, callee_opener(call), f);
|
|
||||||
} else {
|
|
||||||
member.format_trailing_comments(f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Self::TSNonNullExpression(e) => {
|
Self::TSNonNullExpression(e) => {
|
||||||
e.format_leading_comments(f);
|
e.format_leading_comments(f);
|
||||||
@@ -135,6 +135,43 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ChainMember<'a, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes `.b` of `a.b` or `.#b` of `a.#b` with its comments.
|
||||||
|
fn write_member_lookup<'b, 'a, P: GetSpan>(
|
||||||
|
optional: bool,
|
||||||
|
property: AstNode<'b, 'a, P>,
|
||||||
|
f: &mut JsFormatter<'_, 'a>,
|
||||||
|
) where
|
||||||
|
AstNode<'b, 'a, P>: Format<'a, JsFormatContext<'a>>,
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
FormatLeadingComments::Comments(
|
||||||
|
f.context().comments().comments_before(property.span().start)
|
||||||
|
),
|
||||||
|
optional.then_some("?"),
|
||||||
|
".",
|
||||||
|
property
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// The member expression itself
|
||||||
|
let member = property.parent();
|
||||||
|
// The callee of `A.b(c)` / `A.b<T>(c)`: its comments keep their side of the opener
|
||||||
|
if let AstNodes::CallExpression(call) = member.parent()
|
||||||
|
&& !call.optional
|
||||||
|
{
|
||||||
|
write_comments_before_opener(member.span().end, callee_opener(call), f);
|
||||||
|
} else {
|
||||||
|
format_trailing_comments(
|
||||||
|
member.parent().span(),
|
||||||
|
member.span(),
|
||||||
|
property.following_span_start(),
|
||||||
|
)
|
||||||
|
.fmt(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FormatComputedMemberExpressionWithoutObject<'a, 'b>(
|
pub struct FormatComputedMemberExpressionWithoutObject<'a, 'b>(
|
||||||
pub &'b AstNode<'b, 'a, ComputedMemberExpression<'a>>,
|
pub &'b AstNode<'b, 'a, ComputedMemberExpression<'a>>,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -189,14 +189,16 @@ impl<'a, 'b> MemberChainGroup<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn needs_empty_line_before(&self, f: &JsFormatter) -> bool {
|
fn needs_empty_line_before(&self, f: &JsFormatter) -> bool {
|
||||||
let Some(ChainMember::StaticMember(expression)) = self.members.first() else {
|
let Some((object, property_span, _)) =
|
||||||
|
self.members.first().and_then(ChainMember::as_non_computed_member)
|
||||||
|
else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prettier doesn't preserve blank lines after a terminal call like `fn()`
|
// Prettier doesn't preserve blank lines after a terminal call like `fn()`
|
||||||
// whose callee is not part of the chain: `fn()\n\n.bar()` becomes `fn().bar()`.
|
// whose callee is not part of the chain: `fn()\n\n.bar()` becomes `fn().bar()`.
|
||||||
// <https://github.com/prettier/prettier/blob/812a4d0071270f61a7aa549d625b618be7e09d71/src/language-js/print/member-chain.js#L101-L121>
|
// <https://github.com/prettier/prettier/blob/812a4d0071270f61a7aa549d625b618be7e09d71/src/language-js/print/member-chain.js#L101-L121>
|
||||||
if let Expression::CallExpression(call) = expression.object().as_ref()
|
if let Expression::CallExpression(call) = object
|
||||||
&& !call.callee.is_member_expression()
|
&& !call.callee.is_member_expression()
|
||||||
&& !call.callee.is_call_expression()
|
&& !call.callee.is_call_expression()
|
||||||
{
|
{
|
||||||
@@ -208,8 +210,8 @@ impl<'a, 'b> MemberChainGroup<'a, 'b> {
|
|||||||
// `A \n\n/* comment . */ . / comment . */ B`
|
// `A \n\n/* comment . */ . / comment . */ B`
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
// Check whether has more than 1 continuous new lines before the operator (`.`)
|
// Check whether has more than 1 continuous new lines before the operator (`.`)
|
||||||
let start = expression.object().span().end;
|
let start = object.span().end;
|
||||||
let mut end = expression.property().span().start;
|
let mut end = property_span.start;
|
||||||
|
|
||||||
// We should found the real `end` position that is before comments and it is not after the operator `.`
|
// We should found the real `end` position that is before comments and it is not after the operator `.`
|
||||||
if let Some(printed_comment) = f
|
if let Some(printed_comment) = f
|
||||||
|
|||||||
@@ -107,8 +107,10 @@ impl<'a, 'b> MemberChain<'a, 'b> {
|
|||||||
Expression::ThisExpression(_) => true,
|
Expression::ThisExpression(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
} else if let Some(ChainMember::StaticMember(expression)) = self.head.members().last() {
|
} else if let Some((_, _, name)) =
|
||||||
has_computed_property || is_factory(&expression.property().name)
|
self.head.members().last().and_then(ChainMember::as_non_computed_member)
|
||||||
|
{
|
||||||
|
has_computed_property || is_factory(name)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -185,11 +187,10 @@ impl<'a, 'b> MemberChain<'a, 'b> {
|
|||||||
|
|
||||||
/// Check if a member has comments between object and property or end-of-line comments after it
|
/// Check if a member has comments between object and property or end-of-line comments after it
|
||||||
fn has_comment_in_member(member: &ChainMember, comments: &Comments) -> bool {
|
fn has_comment_in_member(member: &ChainMember, comments: &Comments) -> bool {
|
||||||
matches!(
|
member.as_non_computed_member().is_some_and(|(object, property_span, _)| {
|
||||||
member,
|
comments.has_comment_in_range(object.span().end, property_span.start)
|
||||||
ChainMember::StaticMember(member)
|
|| comments.has_end_of_line_comment_after(member.span().end)
|
||||||
if comments.has_comment_in_range(member.object().span().end, member.property().span.start) || comments.has_end_of_line_comment_after(member.span.end)
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_comment(&self, f: &JsFormatter<'_, 'a>) -> bool {
|
fn has_comment(&self, f: &JsFormatter<'_, 'a>) -> bool {
|
||||||
@@ -291,7 +292,9 @@ fn get_split_index_of_head_and_tail_groups(members: &[ChainMember<'_, '_>]) -> u
|
|||||||
.position(|member| {
|
.position(|member| {
|
||||||
!matches!(
|
!matches!(
|
||||||
member,
|
member,
|
||||||
ChainMember::StaticMember { .. } | ChainMember::ComputedMember { .. }
|
ChainMember::StaticMember { .. }
|
||||||
|
| ChainMember::PrivateField { .. }
|
||||||
|
| ChainMember::ComputedMember { .. }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map_or(rest.len(), |index| {
|
.map_or(rest.len(), |index| {
|
||||||
@@ -324,7 +327,9 @@ fn compute_remaining_groups<'a, 'b>(
|
|||||||
ChainMember::ComputedMember(_) if is_computed_array_member_access(&member) => {
|
ChainMember::ComputedMember(_) if is_computed_array_member_access(&member) => {
|
||||||
groups_builder.start_or_continue_group(member);
|
groups_builder.start_or_continue_group(member);
|
||||||
}
|
}
|
||||||
ChainMember::StaticMember(_) | ChainMember::ComputedMember(_) => {
|
ChainMember::StaticMember(_)
|
||||||
|
| ChainMember::PrivateField(_)
|
||||||
|
| ChainMember::ComputedMember(_) => {
|
||||||
// if we have seen a CallExpression, we want to close the group.
|
// if we have seen a CallExpression, we want to close the group.
|
||||||
// The resultant group will be something like: [ . , then, () ];
|
// The resultant group will be something like: [ . , then, () ];
|
||||||
// `.` and `then` belong to the previous StaticMemberExpression,
|
// `.` and `then` belong to the previous StaticMemberExpression,
|
||||||
@@ -420,12 +425,7 @@ fn chain_members_iter<'a, 'b>(
|
|||||||
next: &mut Option<AstNode<'b, 'a, Expression<'a>>>| {
|
next: &mut Option<AstNode<'b, 'a, Expression<'a>>>| {
|
||||||
let callee = expr.callee();
|
let callee = expr.callee();
|
||||||
|
|
||||||
let is_chain = matches!(
|
let is_chain = callee.is_member_expression() || callee.is_call_expression();
|
||||||
callee.as_ref(),
|
|
||||||
Expression::StaticMemberExpression(_)
|
|
||||||
| Expression::ComputedMemberExpression(_)
|
|
||||||
| Expression::CallExpression(_)
|
|
||||||
);
|
|
||||||
|
|
||||||
if is_chain {
|
if is_chain {
|
||||||
*next = Some(callee);
|
*next = Some(callee);
|
||||||
@@ -462,12 +462,7 @@ fn chain_members_iter<'a, 'b>(
|
|||||||
let member = match node {
|
let member = match node {
|
||||||
AstNodeKind::CallExpression(expr) => {
|
AstNodeKind::CallExpression(expr) => {
|
||||||
let callee = expr.callee();
|
let callee = expr.callee();
|
||||||
let is_chain = matches!(
|
let is_chain = callee.is_member_expression() || callee.is_call_expression();
|
||||||
callee.as_ref(),
|
|
||||||
Expression::StaticMemberExpression(_)
|
|
||||||
| Expression::ComputedMemberExpression(_)
|
|
||||||
| Expression::CallExpression(_)
|
|
||||||
);
|
|
||||||
let position = if is_chain {
|
let position = if is_chain {
|
||||||
CallExpressionPosition::Middle
|
CallExpressionPosition::Middle
|
||||||
} else {
|
} else {
|
||||||
@@ -480,6 +475,11 @@ fn chain_members_iter<'a, 'b>(
|
|||||||
next = Some(expr.object());
|
next = Some(expr.object());
|
||||||
ChainMember::StaticMember(expr)
|
ChainMember::StaticMember(expr)
|
||||||
}
|
}
|
||||||
|
AstNodeKind::PrivateFieldExpression(expr) => {
|
||||||
|
let expr = expr.in_arena(allocator);
|
||||||
|
next = Some(expr.object());
|
||||||
|
ChainMember::PrivateField(expr)
|
||||||
|
}
|
||||||
AstNodeKind::ComputedMemberExpression(expr) => {
|
AstNodeKind::ComputedMemberExpression(expr) => {
|
||||||
let expr = expr.in_arena(allocator);
|
let expr = expr.in_arena(allocator);
|
||||||
next = Some(expr.object());
|
next = Some(expr.object());
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
// DIVERGENCES.md#member-property-own-line-comment
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
m() {
|
||||||
|
x = a
|
||||||
|
// comment
|
||||||
|
.b;
|
||||||
|
x = a
|
||||||
|
// comment
|
||||||
|
.#b;
|
||||||
|
return a
|
||||||
|
// comment
|
||||||
|
.#b;
|
||||||
|
}
|
||||||
|
n() {
|
||||||
|
return a
|
||||||
|
// comment
|
||||||
|
.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
source: crates/oxc_formatter/tests/fixtures/mod.rs
|
||||||
|
---
|
||||||
|
==================== Input ====================
|
||||||
|
// DIVERGENCES.md#member-property-own-line-comment
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
m() {
|
||||||
|
x = a
|
||||||
|
// comment
|
||||||
|
.b;
|
||||||
|
x = a
|
||||||
|
// comment
|
||||||
|
.#b;
|
||||||
|
return a
|
||||||
|
// comment
|
||||||
|
.#b;
|
||||||
|
}
|
||||||
|
n() {
|
||||||
|
return a
|
||||||
|
// comment
|
||||||
|
.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
==================== Output ====================
|
||||||
|
------------------
|
||||||
|
{ printWidth: 80 }
|
||||||
|
------------------
|
||||||
|
// DIVERGENCES.md#member-property-own-line-comment
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
m() {
|
||||||
|
x =
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.b;
|
||||||
|
x =
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.#b;
|
||||||
|
return (
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.#b
|
||||||
|
);
|
||||||
|
}
|
||||||
|
n() {
|
||||||
|
return (
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.b
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
{ printWidth: 100 }
|
||||||
|
-------------------
|
||||||
|
// DIVERGENCES.md#member-property-own-line-comment
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
m() {
|
||||||
|
x =
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.b;
|
||||||
|
x =
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.#b;
|
||||||
|
return (
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.#b
|
||||||
|
);
|
||||||
|
}
|
||||||
|
n() {
|
||||||
|
return (
|
||||||
|
a
|
||||||
|
// comment
|
||||||
|
.b
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
===================== End =====================
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
(<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}</div>)?.b;
|
||||||
|
(<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}</div>)?.[0];
|
||||||
|
(<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}</div>)?.b();
|
||||||
|
x = (<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnn}</div>)?.b;
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
source: crates/oxc_formatter/tests/fixtures/mod.rs
|
||||||
|
---
|
||||||
|
==================== Input ====================
|
||||||
|
(<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}</div>)?.b;
|
||||||
|
(<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}</div>)?.[0];
|
||||||
|
(<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}</div>)?.b();
|
||||||
|
x = (<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnn}</div>)?.b;
|
||||||
|
|
||||||
|
==================== Output ====================
|
||||||
|
------------------
|
||||||
|
{ printWidth: 80 }
|
||||||
|
------------------
|
||||||
|
(
|
||||||
|
<div className="some-long-class-name">
|
||||||
|
{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}
|
||||||
|
</div>
|
||||||
|
)?.b;
|
||||||
|
(
|
||||||
|
<div className="some-long-class-name">
|
||||||
|
{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}
|
||||||
|
</div>
|
||||||
|
)?.[0];
|
||||||
|
(
|
||||||
|
<div className="some-long-class-name">
|
||||||
|
{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}
|
||||||
|
</div>
|
||||||
|
)?.b();
|
||||||
|
x = (
|
||||||
|
<div className="some-long-class-name">
|
||||||
|
{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnn}
|
||||||
|
</div>
|
||||||
|
)?.b;
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
{ printWidth: 100 }
|
||||||
|
-------------------
|
||||||
|
(<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}</div>)
|
||||||
|
?.b;
|
||||||
|
(
|
||||||
|
<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}</div>
|
||||||
|
)?.[0];
|
||||||
|
(
|
||||||
|
<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnnn}</div>
|
||||||
|
)?.b();
|
||||||
|
x = (
|
||||||
|
<div className="some-long-class-name">{someVeryLongContentExpressionnnnnnnnnnnnnnnnnnnnnn}</div>
|
||||||
|
)?.b;
|
||||||
|
|
||||||
|
===================== End =====================
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// `a.#b` (class A) is laid out the same as `a._b` (class B)
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
#c;
|
||||||
|
async m() {
|
||||||
|
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb).#b;
|
||||||
|
(await someVeryLongFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThree)).#b;
|
||||||
|
(someCondition ? someVeryLongConsequentValueeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeeeeeeee).#b;
|
||||||
|
(someCondition ? someVeryLongConsequentValueeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeee).#b.method();
|
||||||
|
result = someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeeeeeee.#b;
|
||||||
|
result = someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeee.#b.#c;
|
||||||
|
this.#b.someMethodName().anotherMethodName().yetAnotherMethodName().finalMethodNameeeeeeeeeeeeee();
|
||||||
|
this.#b(argumentNumberOne, argumentNumberTwo, argumentNumberThree, argumentNumberFourrrrrrrrrr);
|
||||||
|
const value = someFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThreeeeeeee).#b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
_b;
|
||||||
|
_c;
|
||||||
|
async m() {
|
||||||
|
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)._b;
|
||||||
|
(await someVeryLongFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThree))._b;
|
||||||
|
(someCondition ? someVeryLongConsequentValueeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeeeeeeee)._b;
|
||||||
|
(someCondition ? someVeryLongConsequentValueeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeee)._b.method();
|
||||||
|
result = someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeeeeeee._b;
|
||||||
|
result = someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeee._b._c;
|
||||||
|
this._b.someMethodName().anotherMethodName().yetAnotherMethodName().finalMethodNameeeeeeeeeeeeee();
|
||||||
|
this._b(argumentNumberOne, argumentNumberTwo, argumentNumberThree, argumentNumberFourrrrrrrrrr);
|
||||||
|
const value = someFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThreeeeeeee)._b;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
---
|
||||||
|
source: crates/oxc_formatter/tests/fixtures/mod.rs
|
||||||
|
---
|
||||||
|
==================== Input ====================
|
||||||
|
// `a.#b` (class A) is laid out the same as `a._b` (class B)
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
#c;
|
||||||
|
async m() {
|
||||||
|
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb).#b;
|
||||||
|
(await someVeryLongFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThree)).#b;
|
||||||
|
(someCondition ? someVeryLongConsequentValueeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeeeeeeee).#b;
|
||||||
|
(someCondition ? someVeryLongConsequentValueeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeee).#b.method();
|
||||||
|
result = someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeeeeeee.#b;
|
||||||
|
result = someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeee.#b.#c;
|
||||||
|
this.#b.someMethodName().anotherMethodName().yetAnotherMethodName().finalMethodNameeeeeeeeeeeeee();
|
||||||
|
this.#b(argumentNumberOne, argumentNumberTwo, argumentNumberThree, argumentNumberFourrrrrrrrrr);
|
||||||
|
const value = someFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThreeeeeeee).#b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
_b;
|
||||||
|
_c;
|
||||||
|
async m() {
|
||||||
|
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)._b;
|
||||||
|
(await someVeryLongFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThree))._b;
|
||||||
|
(someCondition ? someVeryLongConsequentValueeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeeeeeeee)._b;
|
||||||
|
(someCondition ? someVeryLongConsequentValueeeeeeeeeeee : someVeryLongAlternateValueeeeeeeeeee)._b.method();
|
||||||
|
result = someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeeeeeee._b;
|
||||||
|
result = someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeee._b._c;
|
||||||
|
this._b.someMethodName().anotherMethodName().yetAnotherMethodName().finalMethodNameeeeeeeeeeeeee();
|
||||||
|
this._b(argumentNumberOne, argumentNumberTwo, argumentNumberThree, argumentNumberFourrrrrrrrrr);
|
||||||
|
const value = someFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThreeeeeeee)._b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
==================== Output ====================
|
||||||
|
------------------
|
||||||
|
{ printWidth: 80 }
|
||||||
|
------------------
|
||||||
|
// `a.#b` (class A) is laid out the same as `a._b` (class B)
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
#c;
|
||||||
|
async m() {
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
|
).#b;
|
||||||
|
(
|
||||||
|
await someVeryLongFunctionName(
|
||||||
|
argumentNumberOne,
|
||||||
|
argumentNumberTwo,
|
||||||
|
argumentNumberThree,
|
||||||
|
)
|
||||||
|
).#b;
|
||||||
|
(someCondition
|
||||||
|
? someVeryLongConsequentValueeeeeeeeeeee
|
||||||
|
: someVeryLongAlternateValueeeeeeeeeeeeeeee
|
||||||
|
).#b;
|
||||||
|
(someCondition
|
||||||
|
? someVeryLongConsequentValueeeeeeeeeeee
|
||||||
|
: someVeryLongAlternateValueeeeeeeeeee
|
||||||
|
).#b.method();
|
||||||
|
result =
|
||||||
|
someVeryLongObjectName.someVeryLongPropertyName
|
||||||
|
.anotherVeryLongPropertyNameeeeeeeeeee.#b;
|
||||||
|
result =
|
||||||
|
someVeryLongObjectName.someVeryLongPropertyName
|
||||||
|
.anotherVeryLongPropertyNameeeeeee.#b.#c;
|
||||||
|
this.#b
|
||||||
|
.someMethodName()
|
||||||
|
.anotherMethodName()
|
||||||
|
.yetAnotherMethodName()
|
||||||
|
.finalMethodNameeeeeeeeeeeeee();
|
||||||
|
this.#b(
|
||||||
|
argumentNumberOne,
|
||||||
|
argumentNumberTwo,
|
||||||
|
argumentNumberThree,
|
||||||
|
argumentNumberFourrrrrrrrrr,
|
||||||
|
);
|
||||||
|
const value = someFunctionName(
|
||||||
|
argumentNumberOne,
|
||||||
|
argumentNumberTwo,
|
||||||
|
argumentNumberThreeeeeeee,
|
||||||
|
).#b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
_b;
|
||||||
|
_c;
|
||||||
|
async m() {
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
|
)._b;
|
||||||
|
(
|
||||||
|
await someVeryLongFunctionName(
|
||||||
|
argumentNumberOne,
|
||||||
|
argumentNumberTwo,
|
||||||
|
argumentNumberThree,
|
||||||
|
)
|
||||||
|
)._b;
|
||||||
|
(someCondition
|
||||||
|
? someVeryLongConsequentValueeeeeeeeeeee
|
||||||
|
: someVeryLongAlternateValueeeeeeeeeeeeeeee
|
||||||
|
)._b;
|
||||||
|
(someCondition
|
||||||
|
? someVeryLongConsequentValueeeeeeeeeeee
|
||||||
|
: someVeryLongAlternateValueeeeeeeeeee
|
||||||
|
)._b.method();
|
||||||
|
result =
|
||||||
|
someVeryLongObjectName.someVeryLongPropertyName
|
||||||
|
.anotherVeryLongPropertyNameeeeeeeeeee._b;
|
||||||
|
result =
|
||||||
|
someVeryLongObjectName.someVeryLongPropertyName
|
||||||
|
.anotherVeryLongPropertyNameeeeeee._b._c;
|
||||||
|
this._b
|
||||||
|
.someMethodName()
|
||||||
|
.anotherMethodName()
|
||||||
|
.yetAnotherMethodName()
|
||||||
|
.finalMethodNameeeeeeeeeeeeee();
|
||||||
|
this._b(
|
||||||
|
argumentNumberOne,
|
||||||
|
argumentNumberTwo,
|
||||||
|
argumentNumberThree,
|
||||||
|
argumentNumberFourrrrrrrrrr,
|
||||||
|
);
|
||||||
|
const value = someFunctionName(
|
||||||
|
argumentNumberOne,
|
||||||
|
argumentNumberTwo,
|
||||||
|
argumentNumberThreeeeeeee,
|
||||||
|
)._b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
{ printWidth: 100 }
|
||||||
|
-------------------
|
||||||
|
// `a.#b` (class A) is laid out the same as `a._b` (class B)
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
#c;
|
||||||
|
async m() {
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
|
).#b;
|
||||||
|
(await someVeryLongFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThree)).#b;
|
||||||
|
(someCondition
|
||||||
|
? someVeryLongConsequentValueeeeeeeeeeee
|
||||||
|
: someVeryLongAlternateValueeeeeeeeeeeeeeee
|
||||||
|
).#b;
|
||||||
|
(someCondition
|
||||||
|
? someVeryLongConsequentValueeeeeeeeeeee
|
||||||
|
: someVeryLongAlternateValueeeeeeeeeee
|
||||||
|
).#b.method();
|
||||||
|
result =
|
||||||
|
someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeeeeeee.#b;
|
||||||
|
result =
|
||||||
|
someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeee.#b.#c;
|
||||||
|
this.#b
|
||||||
|
.someMethodName()
|
||||||
|
.anotherMethodName()
|
||||||
|
.yetAnotherMethodName()
|
||||||
|
.finalMethodNameeeeeeeeeeeeee();
|
||||||
|
this.#b(argumentNumberOne, argumentNumberTwo, argumentNumberThree, argumentNumberFourrrrrrrrrr);
|
||||||
|
const value = someFunctionName(
|
||||||
|
argumentNumberOne,
|
||||||
|
argumentNumberTwo,
|
||||||
|
argumentNumberThreeeeeeee,
|
||||||
|
).#b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
_b;
|
||||||
|
_c;
|
||||||
|
async m() {
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
|
)._b;
|
||||||
|
(await someVeryLongFunctionName(argumentNumberOne, argumentNumberTwo, argumentNumberThree))._b;
|
||||||
|
(someCondition
|
||||||
|
? someVeryLongConsequentValueeeeeeeeeeee
|
||||||
|
: someVeryLongAlternateValueeeeeeeeeeeeeeee
|
||||||
|
)._b;
|
||||||
|
(someCondition
|
||||||
|
? someVeryLongConsequentValueeeeeeeeeeee
|
||||||
|
: someVeryLongAlternateValueeeeeeeeeee
|
||||||
|
)._b.method();
|
||||||
|
result =
|
||||||
|
someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeeeeeee._b;
|
||||||
|
result =
|
||||||
|
someVeryLongObjectName.someVeryLongPropertyName.anotherVeryLongPropertyNameeeeeee._b._c;
|
||||||
|
this._b
|
||||||
|
.someMethodName()
|
||||||
|
.anotherMethodName()
|
||||||
|
.yetAnotherMethodName()
|
||||||
|
.finalMethodNameeeeeeeeeeeeee();
|
||||||
|
this._b(argumentNumberOne, argumentNumberTwo, argumentNumberThree, argumentNumberFourrrrrrrrrr);
|
||||||
|
const value = someFunctionName(
|
||||||
|
argumentNumberOne,
|
||||||
|
argumentNumberTwo,
|
||||||
|
argumentNumberThreeeeeeee,
|
||||||
|
)._b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
===================== End =====================
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
// DIVERGENCES.md#private-member-like-static
|
||||||
|
class A {
|
||||||
|
static #decorator;
|
||||||
|
static decorator;
|
||||||
|
static #Factory;
|
||||||
|
static Factory;
|
||||||
|
#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking;
|
||||||
|
publicFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreakingXX;
|
||||||
|
|
||||||
|
m() {
|
||||||
|
foo(someIdentifierName.#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking);
|
||||||
|
foo(someIdentifierName.publicFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreakingXX);
|
||||||
|
this.#Factory.someMethodName(argumentOne).anotherMethodName(argumentTwo).third();
|
||||||
|
this.Factory.someMethodName(argumentOneX).anotherMethodName(argumentTwo).third();
|
||||||
|
}
|
||||||
|
|
||||||
|
n(obj) {
|
||||||
|
const a = obj.#decorator(someArgument)((aaaaaaaaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccc) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
const b = obj.decorator(someArgument)((aaaaaaaaaaaa, bbbbbbbbbbbbbbbbb, cccccccccccccccc) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@A.#decorator a() {}
|
||||||
|
@A.decorator b() {}
|
||||||
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
---
|
||||||
|
source: crates/oxc_formatter/tests/fixtures/mod.rs
|
||||||
|
---
|
||||||
|
==================== Input ====================
|
||||||
|
// DIVERGENCES.md#private-member-like-static
|
||||||
|
class A {
|
||||||
|
static #decorator;
|
||||||
|
static decorator;
|
||||||
|
static #Factory;
|
||||||
|
static Factory;
|
||||||
|
#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking;
|
||||||
|
publicFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreakingXX;
|
||||||
|
|
||||||
|
m() {
|
||||||
|
foo(someIdentifierName.#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking);
|
||||||
|
foo(someIdentifierName.publicFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreakingXX);
|
||||||
|
this.#Factory.someMethodName(argumentOne).anotherMethodName(argumentTwo).third();
|
||||||
|
this.Factory.someMethodName(argumentOneX).anotherMethodName(argumentTwo).third();
|
||||||
|
}
|
||||||
|
|
||||||
|
n(obj) {
|
||||||
|
const a = obj.#decorator(someArgument)((aaaaaaaaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccc) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
const b = obj.decorator(someArgument)((aaaaaaaaaaaa, bbbbbbbbbbbbbbbbb, cccccccccccccccc) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@A.#decorator a() {}
|
||||||
|
@A.decorator b() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
==================== Output ====================
|
||||||
|
------------------
|
||||||
|
{ printWidth: 80 }
|
||||||
|
------------------
|
||||||
|
// DIVERGENCES.md#private-member-like-static
|
||||||
|
class A {
|
||||||
|
static #decorator;
|
||||||
|
static decorator;
|
||||||
|
static #Factory;
|
||||||
|
static Factory;
|
||||||
|
#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking;
|
||||||
|
publicFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreakingXX;
|
||||||
|
|
||||||
|
m() {
|
||||||
|
foo(
|
||||||
|
someIdentifierName.#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking,
|
||||||
|
);
|
||||||
|
foo(
|
||||||
|
someIdentifierName.publicFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreakingXX,
|
||||||
|
);
|
||||||
|
this.#Factory.someMethodName(argumentOne)
|
||||||
|
.anotherMethodName(argumentTwo)
|
||||||
|
.third();
|
||||||
|
this.Factory.someMethodName(argumentOneX)
|
||||||
|
.anotherMethodName(argumentTwo)
|
||||||
|
.third();
|
||||||
|
}
|
||||||
|
|
||||||
|
n(obj) {
|
||||||
|
const a = obj.#decorator(someArgument)((
|
||||||
|
aaaaaaaaaaaa,
|
||||||
|
bbbbbbbbbbbbbbbb,
|
||||||
|
cccccccccccccccc,
|
||||||
|
) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
const b = obj.decorator(someArgument)((
|
||||||
|
aaaaaaaaaaaa,
|
||||||
|
bbbbbbbbbbbbbbbbb,
|
||||||
|
cccccccccccccccc,
|
||||||
|
) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@A.#decorator a() {}
|
||||||
|
@A.decorator b() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
{ printWidth: 100 }
|
||||||
|
-------------------
|
||||||
|
// DIVERGENCES.md#private-member-like-static
|
||||||
|
class A {
|
||||||
|
static #decorator;
|
||||||
|
static decorator;
|
||||||
|
static #Factory;
|
||||||
|
static Factory;
|
||||||
|
#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking;
|
||||||
|
publicFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreakingXX;
|
||||||
|
|
||||||
|
m() {
|
||||||
|
foo(someIdentifierName.#privateFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreaking);
|
||||||
|
foo(someIdentifierName.publicFieldWithAVeryLongNameThatDoesNotFitOnTheLineEvenAfterBreakingXX);
|
||||||
|
this.#Factory.someMethodName(argumentOne).anotherMethodName(argumentTwo).third();
|
||||||
|
this.Factory.someMethodName(argumentOneX).anotherMethodName(argumentTwo).third();
|
||||||
|
}
|
||||||
|
|
||||||
|
n(obj) {
|
||||||
|
const a = obj.#decorator(someArgument)((aaaaaaaaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccc) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
const b = obj.decorator(someArgument)((aaaaaaaaaaaa, bbbbbbbbbbbbbbbbb, cccccccccccccccc) => {
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@A.#decorator a() {}
|
||||||
|
@A.decorator b() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
===================== End =====================
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// `a.#b` (class A) is laid out the same as `a._b` (class B)
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
m() {
|
||||||
|
(someVeryLongExpressionName as unknown as SomeVeryLongTypeName<WithSomeTypeArgumentsss>).#b;
|
||||||
|
(someVeryLongExpressionName satisfies SomeVeryLongTypeName<WithSomeTypeArgumentsssssss>).#b;
|
||||||
|
(<SomeVeryLongTypeName<WithSomeTypeArguments>>someVeryLongExpressionNameeeeeeeeeeeeeeeeeee).#b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
_b;
|
||||||
|
m() {
|
||||||
|
(someVeryLongExpressionName as unknown as SomeVeryLongTypeName<WithSomeTypeArgumentsss>)._b;
|
||||||
|
(someVeryLongExpressionName satisfies SomeVeryLongTypeName<WithSomeTypeArgumentsssssss>)._b;
|
||||||
|
(<SomeVeryLongTypeName<WithSomeTypeArguments>>someVeryLongExpressionNameeeeeeeeeeeeeeeeeee)._b;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
source: crates/oxc_formatter/tests/fixtures/mod.rs
|
||||||
|
---
|
||||||
|
==================== Input ====================
|
||||||
|
// `a.#b` (class A) is laid out the same as `a._b` (class B)
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
m() {
|
||||||
|
(someVeryLongExpressionName as unknown as SomeVeryLongTypeName<WithSomeTypeArgumentsss>).#b;
|
||||||
|
(someVeryLongExpressionName satisfies SomeVeryLongTypeName<WithSomeTypeArgumentsssssss>).#b;
|
||||||
|
(<SomeVeryLongTypeName<WithSomeTypeArguments>>someVeryLongExpressionNameeeeeeeeeeeeeeeeeee).#b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
_b;
|
||||||
|
m() {
|
||||||
|
(someVeryLongExpressionName as unknown as SomeVeryLongTypeName<WithSomeTypeArgumentsss>)._b;
|
||||||
|
(someVeryLongExpressionName satisfies SomeVeryLongTypeName<WithSomeTypeArgumentsssssss>)._b;
|
||||||
|
(<SomeVeryLongTypeName<WithSomeTypeArguments>>someVeryLongExpressionNameeeeeeeeeeeeeeeeeee)._b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
==================== Output ====================
|
||||||
|
------------------
|
||||||
|
{ printWidth: 80 }
|
||||||
|
------------------
|
||||||
|
// `a.#b` (class A) is laid out the same as `a._b` (class B)
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
m() {
|
||||||
|
(
|
||||||
|
someVeryLongExpressionName as unknown as SomeVeryLongTypeName<WithSomeTypeArgumentsss>
|
||||||
|
).#b;
|
||||||
|
(
|
||||||
|
someVeryLongExpressionName satisfies SomeVeryLongTypeName<WithSomeTypeArgumentsssssss>
|
||||||
|
).#b;
|
||||||
|
(<SomeVeryLongTypeName<WithSomeTypeArguments>>(
|
||||||
|
someVeryLongExpressionNameeeeeeeeeeeeeeeeeee
|
||||||
|
)).#b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
_b;
|
||||||
|
m() {
|
||||||
|
(
|
||||||
|
someVeryLongExpressionName as unknown as SomeVeryLongTypeName<WithSomeTypeArgumentsss>
|
||||||
|
)._b;
|
||||||
|
(
|
||||||
|
someVeryLongExpressionName satisfies SomeVeryLongTypeName<WithSomeTypeArgumentsssssss>
|
||||||
|
)._b;
|
||||||
|
(<SomeVeryLongTypeName<WithSomeTypeArguments>>(
|
||||||
|
someVeryLongExpressionNameeeeeeeeeeeeeeeeeee
|
||||||
|
))._b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
{ printWidth: 100 }
|
||||||
|
-------------------
|
||||||
|
// `a.#b` (class A) is laid out the same as `a._b` (class B)
|
||||||
|
class A {
|
||||||
|
#b;
|
||||||
|
m() {
|
||||||
|
(someVeryLongExpressionName as unknown as SomeVeryLongTypeName<WithSomeTypeArgumentsss>).#b;
|
||||||
|
(someVeryLongExpressionName satisfies SomeVeryLongTypeName<WithSomeTypeArgumentsssssss>).#b;
|
||||||
|
(<SomeVeryLongTypeName<WithSomeTypeArguments>>someVeryLongExpressionNameeeeeeeeeeeeeeeeeee).#b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
_b;
|
||||||
|
m() {
|
||||||
|
(someVeryLongExpressionName as unknown as SomeVeryLongTypeName<WithSomeTypeArgumentsss>)._b;
|
||||||
|
(someVeryLongExpressionName satisfies SomeVeryLongTypeName<WithSomeTypeArgumentsssssss>)._b;
|
||||||
|
(<SomeVeryLongTypeName<WithSomeTypeArguments>>someVeryLongExpressionNameeeeeeeeeeeeeeeeeee)._b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
===================== End =====================
|
||||||
@@ -33,14 +33,14 @@ RadixUIAdoptionSection.jsx:
|
|||||||
|
|
||||||
pdf.mjs:
|
pdf.mjs:
|
||||||
file size: 567296 # 567.30 kB
|
file size: 567296 # 567.30 kB
|
||||||
sys allocs: 9736
|
sys allocs: 9755
|
||||||
sys reallocs: 167
|
sys reallocs: 167
|
||||||
sys deallocs: 9735
|
sys deallocs: 9754
|
||||||
sys alloc bytes: 2384856 # 2.38 MB
|
sys alloc bytes: 2384856 # 2.38 MB
|
||||||
sys peak growth: 1156488 # 1.16 MB
|
sys peak growth: 1156488 # 1.16 MB
|
||||||
arena allocs: 20583
|
arena allocs: 21673
|
||||||
arena reallocs: 428
|
arena reallocs: 446
|
||||||
arena size: 11029152 # 11.03 MB
|
arena size: 11090656 # 11.09 MB
|
||||||
|
|
||||||
antd.js:
|
antd.js:
|
||||||
file size: 6686316 # 6.69 MB
|
file size: 6686316 # 6.69 MB
|
||||||
@@ -71,6 +71,6 @@ kitchen-sink.tsx:
|
|||||||
sys deallocs: 12609
|
sys deallocs: 12609
|
||||||
sys alloc bytes: 3737938 # 3.74 MB
|
sys alloc bytes: 3737938 # 3.74 MB
|
||||||
sys peak growth: 1491884 # 1.49 MB
|
sys peak growth: 1491884 # 1.49 MB
|
||||||
arena allocs: 30666
|
arena allocs: 30688
|
||||||
arena reallocs: 1041
|
arena reallocs: 1043
|
||||||
arena size: 14614680 # 14.61 MB
|
arena size: 14615800 # 14.62 MB
|
||||||
Reference in new issue
Block a user