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.
|
||||
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.
|
||||
|
||||
## 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`
|
||||
pub fn is_binary_like(&self) -> bool {
|
||||
matches!(
|
||||
|
||||
@@ -1196,9 +1196,6 @@ fn jsx_element_or_fragment_needs_paren(span: Span, parent: &AstNodes<'_, '_>) ->
|
||||
AstNodes::TSAsExpression(_)
|
||||
| AstNodes::TSSatisfiesExpression(_)
|
||||
| AstNodes::AwaitExpression(_)
|
||||
| AstNodes::StaticMemberExpression(_)
|
||||
| AstNodes::ComputedMemberExpression(_)
|
||||
| AstNodes::PrivateFieldExpression(_)
|
||||
| AstNodes::SequenceExpression(_)
|
||||
| AstNodes::UnaryExpression(_)
|
||||
| AstNodes::TSNonNullExpression(_)
|
||||
@@ -1206,8 +1203,7 @@ fn jsx_element_or_fragment_needs_paren(span: Span, parent: &AstNodes<'_, '_>) ->
|
||||
| AstNodes::TaggedTemplateExpression(_)
|
||||
| AstNodes::JSXSpreadAttribute(_)
|
||||
| AstNodes::JSXSpreadChild(_) => true,
|
||||
_ if parent.is_call_like_callee_span(span) => true,
|
||||
_ => false,
|
||||
_ => parent.is_member_expression() || parent.is_call_like_callee_span(span),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,15 +132,10 @@ fn format_as_or_satisfies_expression<'a>(
|
||||
}
|
||||
|
||||
fn is_callee_or_object_context(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||
match parent {
|
||||
// Static member
|
||||
AstNodes::StaticMemberExpression(_) => true,
|
||||
AstNodes::ComputedMemberExpression(member) => member.object.span() == span,
|
||||
parent.is_member_object_span(span)
|
||||
// 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
|
||||
// NOTE: We may revert this if resolved: https://github.com/prettier/prettier/issues/18406
|
||||
// _ => parent.is_call_like_callee_span(span),
|
||||
AstNodes::CallExpression(call) => call.callee.span() == span,
|
||||
_ => false,
|
||||
}
|
||||
// || parent.is_call_like_callee_span(span)
|
||||
|| matches!(parent, AstNodes::CallExpression(call) if call.callee.span() == span)
|
||||
}
|
||||
@@ -230,8 +230,11 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLikeExpression<'a, '_> {
|
||||
AstNodes::UnaryExpression(unary) if unary_argument_takes_comment_parens(unary, f) => {
|
||||
return format_flattened_logical_expression(*self, false, f);
|
||||
}
|
||||
AstNodes::StaticMemberExpression(_) | AstNodes::UnaryExpression(_) => true,
|
||||
_ => parent.is_call_like_callee_span(self.span()),
|
||||
AstNodes::UnaryExpression(_) => true,
|
||||
_ => {
|
||||
parent.is_non_computed_member_expression()
|
||||
|| parent.is_call_like_callee_span(self.span())
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
let decorator = &callee_call.callee;
|
||||
let is_valid_decorator = matches!(decorator, Expression::Identifier(_))
|
||||
|| matches!(
|
||||
decorator,
|
||||
Expression::StaticMemberExpression(member)
|
||||
if matches!(&member.object, Expression::Identifier(_))
|
||||
);
|
||||
let is_valid_decorator = match decorator {
|
||||
Expression::Identifier(_) => true,
|
||||
Expression::StaticMemberExpression(member) => {
|
||||
matches!(&member.object, Expression::Identifier(_))
|
||||
}
|
||||
Expression::PrivateFieldExpression(member) => {
|
||||
matches!(&member.object, Expression::Identifier(_))
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !is_valid_decorator {
|
||||
return false;
|
||||
|
||||
@@ -42,10 +42,8 @@ impl<'a> FormatWrite<'a> for AstNode<'_, 'a, CallExpression<'a>> {
|
||||
None
|
||||
};
|
||||
|
||||
if matches!(
|
||||
callee.as_ref(),
|
||||
Expression::StaticMemberExpression(_) | Expression::ComputedMemberExpression(_)
|
||||
) && !is_verbatim_multiline_template_sole_arg(arguments.as_slice(), Some(self), f)
|
||||
if callee.is_member_expression()
|
||||
&& !is_verbatim_multiline_template_sole_arg(arguments.as_slice(), Some(self), f)
|
||||
&& !is_simple_module_import(&self.arguments(), 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;
|
||||
loop {
|
||||
match expr {
|
||||
@@ -60,6 +60,9 @@ fn is_identifier_or_static_member_only(callee: &Expression) -> bool {
|
||||
Expression::StaticMemberExpression(static_member) => {
|
||||
expr = &static_member.object;
|
||||
}
|
||||
Expression::PrivateFieldExpression(private_field) => {
|
||||
expr = &private_field.object;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
@@ -73,16 +76,13 @@ impl<'a> FormatWrite<'a> for AstNode<'_, 'a, Decorator<'a>> {
|
||||
|
||||
// Determine if parentheses are required around decorator expressions
|
||||
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
|
||||
Expression::CallExpression(call) => !is_identifier_or_static_member_only(&call.callee),
|
||||
// Static member expressions: `@obj.prop` needs no parens, `@(complex[key])` needs parens
|
||||
Expression::StaticMemberExpression(static_member) => {
|
||||
!is_identifier_or_static_member_only(&static_member.object)
|
||||
Expression::CallExpression(call) => {
|
||||
!is_identifier_or_non_computed_member_only(&call.callee)
|
||||
}
|
||||
// All other expressions need parentheses: `@(a + b)`, `@(condition ? a : b)`
|
||||
_ => true,
|
||||
// `@decorator`, `@obj.prop` and `@obj.#prop` need no parens,
|
||||
// all other expressions need parens: `@(complex[key])`, `@(a + b)`
|
||||
expression => !is_identifier_or_non_computed_member_only(expression),
|
||||
};
|
||||
|
||||
if needs_parentheses {
|
||||
|
||||
@@ -95,25 +95,11 @@ impl<'a> AnyJsxTagWithChildren<'a, '_> {
|
||||
| AstNodes::JSXExpressionContainer(_)
|
||||
| AstNodes::ConditionalExpression(_)
|
||||
| AstNodes::ExpressionStatement(_) => WrapState::NoWrap,
|
||||
AstNodes::StaticMemberExpression(member) => {
|
||||
if member.optional {
|
||||
WrapState::NoWrap
|
||||
} else {
|
||||
WrapState::WrapOnBreak
|
||||
}
|
||||
}
|
||||
// It is a argument of a call expression
|
||||
AstNodes::CallExpression(call) if call.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>`
|
||||
#[expect(clippy::match_same_arms)]
|
||||
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>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
// Before the recording: printing the object consumes its cast comment
|
||||
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))
|
||||
};
|
||||
write_non_computed_member(self.object(), self.optional(), self.property(), f);
|
||||
}
|
||||
}
|
||||
|
||||
match layout(self, is_member_chain, object_is_cast_target, f) {
|
||||
StaticMemberLayout::NoBreak => {
|
||||
let format_no_break =
|
||||
format_with(|f| write!(f, [operator_token(self.optional()), self.property()]));
|
||||
/// `a.#b` is laid out the same as `a.b`.
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, PrivateFieldExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write_non_computed_member(self.object(), self.optional(), self.field(), f);
|
||||
}
|
||||
}
|
||||
|
||||
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(self.property.span.start) {
|
||||
let comments = f
|
||||
.context()
|
||||
.comments()
|
||||
.comments_before(self.property.span.start);
|
||||
write!(
|
||||
f,
|
||||
[FormatLeadingComments::Comments(comments), soft_line_break()]
|
||||
);
|
||||
}
|
||||
}),
|
||||
operator_token(self.optional()),
|
||||
self.property(),
|
||||
)))]
|
||||
);
|
||||
/// Writes `a.b` or `a.#b`.
|
||||
fn write_non_computed_member<'b, 'a, P: GetSpan>(
|
||||
object: AstNode<'b, 'a, Expression<'a>>,
|
||||
optional: bool,
|
||||
property: AstNode<'b, 'a, P>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) where
|
||||
AstNode<'b, 'a, P>: Format<'a, JsFormatContext<'a>>,
|
||||
{
|
||||
// Before the recording: printing the object consumes its cast comment
|
||||
let object_is_cast_target = is_cast_target(object.span(), f);
|
||||
let is_member_chain = {
|
||||
let start = f.elements().len();
|
||||
write!(f, [object]);
|
||||
f.elements()[start..].has_label(LabelId::of(JsLabels::MemberChain))
|
||||
};
|
||||
let property_start = property.span().start;
|
||||
// The member expression itself
|
||||
let member = property.parent();
|
||||
|
||||
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]));
|
||||
|
||||
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`:
|
||||
/// a cast-target object is neither a call nor an identifier to the rules below (see `is_cast_target`).
|
||||
fn layout<'a>(
|
||||
node: &AstNode<'_, 'a, StaticMemberExpression<'a>>,
|
||||
object: &Expression<'a>,
|
||||
property_start: u32,
|
||||
member: &AstNodes<'_, 'a>,
|
||||
is_member_chain: bool,
|
||||
object_is_cast_target: bool,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> 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;
|
||||
}
|
||||
|
||||
// `a.b.c!` and `a.b?.c`
|
||||
// `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.
|
||||
let mut parent = node.parent();
|
||||
let mut parent = member.parent();
|
||||
while matches!(parent, AstNodes::TSNonNullExpression(_) | AstNodes::ChainExpression(_)) {
|
||||
parent = parent.parent();
|
||||
}
|
||||
let object = &node.object;
|
||||
|
||||
let is_nested = match parent {
|
||||
AstNodes::AssignmentExpression(_) | AstNodes::VariableDeclarator(_) => {
|
||||
@@ -117,8 +136,7 @@ fn layout<'a>(
|
||||
|
||||
false
|
||||
}
|
||||
AstNodes::StaticMemberExpression(_) | AstNodes::ComputedMemberExpression(_) => true,
|
||||
_ => false,
|
||||
_ => parent.is_member_expression(),
|
||||
};
|
||||
|
||||
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;
|
||||
while matches!(
|
||||
first_non_static_member_ancestor,
|
||||
AstNodes::StaticMemberExpression(_)
|
||||
| AstNodes::ComputedMemberExpression(_)
|
||||
| AstNodes::ChainExpression(_)
|
||||
| AstNodes::TSNonNullExpression(_)
|
||||
) {
|
||||
while first_non_static_member_ancestor.is_member_expression()
|
||||
|| matches!(
|
||||
first_non_static_member_ancestor,
|
||||
AstNodes::ChainExpression(_) | AstNodes::TSNonNullExpression(_)
|
||||
)
|
||||
{
|
||||
first_non_static_member_ancestor = first_non_static_member_ancestor.parent();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
AstNodes::AssignmentExpression(assignment) => {
|
||||
@@ -150,9 +167,3 @@ fn layout<'a>(
|
||||
_ => 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>) {
|
||||
let format_inner = format_with(|f| write!(f, ["await", space(), self.argument()]));
|
||||
|
||||
let is_callee_or_object = match self.parent() {
|
||||
AstNodes::StaticMemberExpression(_) => true,
|
||||
AstNodes::ComputedMemberExpression(member) => member.object.span() == self.span(),
|
||||
_ => self.parent().is_call_like_callee_span(self.span),
|
||||
};
|
||||
let is_callee_or_object = self.parent().is_member_object_span(self.span)
|
||||
|| self.parent().is_call_like_callee_span(self.span);
|
||||
|
||||
if !is_callee_or_object {
|
||||
write!(f, [format_inner]);
|
||||
|
||||
@@ -210,25 +210,18 @@ fn has_argument_leading_comments(argument: &AstNode<Expression>, f: &JsFormatter
|
||||
// This check is based on
|
||||
// <https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/language-js/comments/handle-comments.js#L335-L349>
|
||||
if let LeftSide::Expression(left_side) = left_side {
|
||||
let has_leading_own_line_comment = match left_side {
|
||||
Expression::ChainExpression(chain) => {
|
||||
if let ChainElement::StaticMemberExpression(member) = &chain.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,
|
||||
let member = match left_side {
|
||||
Expression::ChainExpression(chain) => chain.expression.as_member_expression(),
|
||||
_ => left_side.as_member_expression(),
|
||||
};
|
||||
// `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 {
|
||||
return true;
|
||||
|
||||
@@ -270,21 +270,9 @@ impl<'a> FormatConditionalLike<'a, '_> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
AstNodes::StaticMemberExpression(member) => {
|
||||
if member.object.span() == expression_span {
|
||||
expression_span = member.span();
|
||||
parent = member.parent();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
AstNodes::ComputedMemberExpression(member) => {
|
||||
if member.object.span() == expression_span {
|
||||
expression_span = member.span();
|
||||
parent = member.parent();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
member if member.is_member_object_span(expression_span) => {
|
||||
expression_span = member.span();
|
||||
parent = member.parent();
|
||||
}
|
||||
AstNodes::CallExpression(call) => {
|
||||
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]
|
||||
fn is_parent_static_member_expression(&self, layout: ConditionalLayout) -> bool {
|
||||
layout.is_root()
|
||||
&& self.is_conditional_expression()
|
||||
&& matches!(self.parent(), AstNodes::StaticMemberExpression(_))
|
||||
&& self.parent().is_non_computed_member_expression()
|
||||
}
|
||||
|
||||
/// Formats the test part of the conditional
|
||||
|
||||
@@ -3,25 +3,27 @@ use std::ops::Deref;
|
||||
use crate::{
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
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},
|
||||
write,
|
||||
};
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_formatter_core::Format;
|
||||
use oxc_span::GetSpan;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CallExpressionPosition {
|
||||
/// At the start of a call chain.
|
||||
/// `of` in `of().test`
|
||||
Start,
|
||||
|
||||
/// Somewhere in the middle.
|
||||
///
|
||||
/// `b` in `a.b().c()`
|
||||
Middle,
|
||||
|
||||
/// At the end of a call chain (root)
|
||||
/// `c` in `a.b.c()`
|
||||
End,
|
||||
@@ -32,24 +34,22 @@ pub enum CallExpressionPosition {
|
||||
pub enum ChainMember<'a, 'b> {
|
||||
/// Holds onto a [oxc_ast::ast::StaticMemberExpression]
|
||||
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]
|
||||
CallExpression {
|
||||
expression: &'b AstNode<'b, 'a, CallExpression<'a>>,
|
||||
position: CallExpressionPosition,
|
||||
},
|
||||
|
||||
/// Holds onto a [oxc_ast::ast::ComputedMemberExpression]
|
||||
ComputedMember(&'b AstNode<'b, 'a, ComputedMemberExpression<'a>>),
|
||||
|
||||
TSNonNullExpression(&'b AstNode<'b, 'a, TSNonNullExpression<'a>>),
|
||||
|
||||
/// Any other node that are not [oxc_ast::ast::CallExpression] or [oxc_ast::ast::StaticMemberExpression]
|
||||
/// Any other node that is not one of the above
|
||||
/// Are tracked using this variant
|
||||
Node(&'b AstNode<'b, 'a, Expression<'a>>),
|
||||
}
|
||||
|
||||
impl ChainMember<'_, '_> {
|
||||
impl<'a> ChainMember<'a, '_> {
|
||||
pub(crate) const fn is_call_expression(&self) -> bool {
|
||||
matches!(self, Self::CallExpression { .. })
|
||||
}
|
||||
@@ -58,9 +58,25 @@ impl ChainMember<'_, '_> {
|
||||
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 {
|
||||
match self {
|
||||
Self::StaticMember(member) => member.span(),
|
||||
Self::PrivateField(member) => member.span(),
|
||||
Self::TSNonNullExpression(e) => e.span(),
|
||||
Self::CallExpression { expression, .. } => expression.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>) {
|
||||
match self {
|
||||
Self::StaticMember(member) => {
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
FormatLeadingComments::Comments(
|
||||
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);
|
||||
}
|
||||
write_member_lookup(member.optional(), member.property(), f);
|
||||
}
|
||||
Self::PrivateField(member) => {
|
||||
write_member_lookup(member.optional(), member.field(), f);
|
||||
}
|
||||
Self::TSNonNullExpression(e) => {
|
||||
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 &'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 {
|
||||
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;
|
||||
};
|
||||
|
||||
// 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()`.
|
||||
// <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_call_expression()
|
||||
{
|
||||
@@ -208,8 +210,8 @@ impl<'a, 'b> MemberChainGroup<'a, 'b> {
|
||||
// `A \n\n/* comment . */ . / comment . */ B`
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// Check whether has more than 1 continuous new lines before the operator (`.`)
|
||||
let start = expression.object().span().end;
|
||||
let mut end = expression.property().span().start;
|
||||
let start = object.span().end;
|
||||
let mut end = property_span.start;
|
||||
|
||||
// We should found the real `end` position that is before comments and it is not after the operator `.`
|
||||
if let Some(printed_comment) = f
|
||||
|
||||
@@ -107,8 +107,10 @@ impl<'a, 'b> MemberChain<'a, 'b> {
|
||||
Expression::ThisExpression(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
} else if let Some(ChainMember::StaticMember(expression)) = self.head.members().last() {
|
||||
has_computed_property || is_factory(&expression.property().name)
|
||||
} else if let Some((_, _, name)) =
|
||||
self.head.members().last().and_then(ChainMember::as_non_computed_member)
|
||||
{
|
||||
has_computed_property || is_factory(name)
|
||||
} else {
|
||||
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
|
||||
fn has_comment_in_member(member: &ChainMember, comments: &Comments) -> bool {
|
||||
matches!(
|
||||
member,
|
||||
ChainMember::StaticMember(member)
|
||||
if comments.has_comment_in_range(member.object().span().end, member.property().span.start) || comments.has_end_of_line_comment_after(member.span.end)
|
||||
)
|
||||
member.as_non_computed_member().is_some_and(|(object, property_span, _)| {
|
||||
comments.has_comment_in_range(object.span().end, property_span.start)
|
||||
|| comments.has_end_of_line_comment_after(member.span().end)
|
||||
})
|
||||
}
|
||||
|
||||
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| {
|
||||
!matches!(
|
||||
member,
|
||||
ChainMember::StaticMember { .. } | ChainMember::ComputedMember { .. }
|
||||
ChainMember::StaticMember { .. }
|
||||
| ChainMember::PrivateField { .. }
|
||||
| ChainMember::ComputedMember { .. }
|
||||
)
|
||||
})
|
||||
.map_or(rest.len(), |index| {
|
||||
@@ -324,7 +327,9 @@ fn compute_remaining_groups<'a, 'b>(
|
||||
ChainMember::ComputedMember(_) if is_computed_array_member_access(&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.
|
||||
// The resultant group will be something like: [ . , then, () ];
|
||||
// `.` 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>>>| {
|
||||
let callee = expr.callee();
|
||||
|
||||
let is_chain = matches!(
|
||||
callee.as_ref(),
|
||||
Expression::StaticMemberExpression(_)
|
||||
| Expression::ComputedMemberExpression(_)
|
||||
| Expression::CallExpression(_)
|
||||
);
|
||||
let is_chain = callee.is_member_expression() || callee.is_call_expression();
|
||||
|
||||
if is_chain {
|
||||
*next = Some(callee);
|
||||
@@ -462,12 +462,7 @@ fn chain_members_iter<'a, 'b>(
|
||||
let member = match node {
|
||||
AstNodeKind::CallExpression(expr) => {
|
||||
let callee = expr.callee();
|
||||
let is_chain = matches!(
|
||||
callee.as_ref(),
|
||||
Expression::StaticMemberExpression(_)
|
||||
| Expression::ComputedMemberExpression(_)
|
||||
| Expression::CallExpression(_)
|
||||
);
|
||||
let is_chain = callee.is_member_expression() || callee.is_call_expression();
|
||||
let position = if is_chain {
|
||||
CallExpressionPosition::Middle
|
||||
} else {
|
||||
@@ -480,6 +475,11 @@ fn chain_members_iter<'a, 'b>(
|
||||
next = Some(expr.object());
|
||||
ChainMember::StaticMember(expr)
|
||||
}
|
||||
AstNodeKind::PrivateFieldExpression(expr) => {
|
||||
let expr = expr.in_arena(allocator);
|
||||
next = Some(expr.object());
|
||||
ChainMember::PrivateField(expr)
|
||||
}
|
||||
AstNodeKind::ComputedMemberExpression(expr) => {
|
||||
let expr = expr.in_arena(allocator);
|
||||
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:
|
||||
file size: 567296 # 567.30 kB
|
||||
sys allocs: 9736
|
||||
sys allocs: 9755
|
||||
sys reallocs: 167
|
||||
sys deallocs: 9735
|
||||
sys deallocs: 9754
|
||||
sys alloc bytes: 2384856 # 2.38 MB
|
||||
sys peak growth: 1156488 # 1.16 MB
|
||||
arena allocs: 20583
|
||||
arena reallocs: 428
|
||||
arena size: 11029152 # 11.03 MB
|
||||
arena allocs: 21673
|
||||
arena reallocs: 446
|
||||
arena size: 11090656 # 11.09 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
@@ -71,6 +71,6 @@ kitchen-sink.tsx:
|
||||
sys deallocs: 12609
|
||||
sys alloc bytes: 3737938 # 3.74 MB
|
||||
sys peak growth: 1491884 # 1.49 MB
|
||||
arena allocs: 30666
|
||||
arena reallocs: 1041
|
||||
arena size: 14614680 # 14.61 MB
|
||||
arena allocs: 30688
|
||||
arena reallocs: 1043
|
||||
arena size: 14615800 # 14.62 MB
|
||||
Reference in new issue
Block a user