Skip to content

Commit 8ccfb06

Browse files
committed
fix(formatter): should indent class extends and interface heritage when it is a member expression without type arguments (#16323)
* close: #16259
1 parent 0faa978 commit 8ccfb06

File tree

5 files changed

+64
-6
lines changed

5 files changed

+64
-6
lines changed

crates/oxc_formatter/src/write/class.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,13 +469,30 @@ impl<'a> Format<'a> for FormatClass<'a, '_> {
469469
///
470470
/// Heritage clauses are grouped when:
471471
/// 1. Superclass and/or implements are more than one
472-
/// 2. There are comments in the heritage clause area
473-
/// 3. There are trailing line comments after type parameters
472+
/// 2. The superclass is a member expression without type arguments, or the implements clause is a qualified name without type arguments
473+
/// 3. There are comments in the heritage clause area
474+
/// 4. There are trailing line comments after type parameters
474475
fn should_group<'a>(class: &Class<'a>, f: &Formatter<'_, 'a>) -> bool {
475476
if usize::from(class.super_class.is_some()) + class.implements.len() > 1 {
476477
return true;
477478
}
478479

480+
if !class.is_expression()
481+
&& ((class.super_type_arguments.is_none()
482+
&& class
483+
.super_class
484+
.as_ref()
485+
.is_some_and(|super_class|
486+
super_class.is_member_expression() ||
487+
matches!(&super_class, Expression::ChainExpression(chain) if chain.expression.is_member_expression())
488+
))
489+
|| class.implements.first().is_some_and(|implements| {
490+
implements.type_arguments.is_none() && implements.expression.is_qualified_name()
491+
}))
492+
{
493+
return true;
494+
}
495+
479496
let comments = f.comments();
480497

481498
let id_span = class.id.as_ref().map(GetSpan::span);

crates/oxc_formatter/src/write/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,10 +1301,16 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSInterfaceDeclaration<'a>> {
13011301
let extends = self.extends();
13021302
let body = self.body();
13031303

1304+
// Determines whether to use group mode for formatting the `extends` clause.
1305+
// 1. If there are multiple `extends`, we always use group mode.
1306+
// 2. If there is a single `extends` that is a member expression without type arguments, we use group mode.
1307+
// 3. If there are comments between the `id` and the `extends`, we use group mode.
13041308
let group_mode = extends.len() > 1
13051309
|| extends.as_ref().first().is_some_and(|first| {
1306-
let prev_span = type_parameters.as_ref().map_or(id.span(), GetSpan::span);
1307-
f.comments().has_comment_in_range(prev_span.end, first.span().start)
1310+
(first.expression.is_member_expression() && first.type_arguments.is_none()) || {
1311+
let prev_span = type_parameters.as_ref().map_or(id.span(), GetSpan::span);
1312+
f.comments().has_comment_in_range(prev_span.end, first.span().start)
1313+
}
13081314
});
13091315

13101316
let format_id = format_with(|f| {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export class longlonglonglonglonglonglonglonglonglonglongclassname
2+
extends someobjectsomepropertysomeotherproperty.SomeClass {}
3+
4+
export interface longlonglonglonglonglonglonglonglonglonglongclassname
5+
extends someobjectsomepropertysomeotherproperty.SomeClass {}
6+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
source: crates/oxc_formatter/tests/fixtures/mod.rs
3+
---
4+
==================== Input ====================
5+
export class longlonglonglonglonglonglonglonglonglonglongclassname
6+
extends someobjectsomepropertysomeotherproperty.SomeClass {}
7+
8+
export interface longlonglonglonglonglonglonglonglonglonglongclassname
9+
extends someobjectsomepropertysomeotherproperty.SomeClass {}
10+
11+
==================== Output ====================
12+
------------------
13+
{ printWidth: 80 }
14+
------------------
15+
export class longlonglonglonglonglonglonglonglonglonglongclassname
16+
extends someobjectsomepropertysomeotherproperty.SomeClass {}
17+
18+
export interface longlonglonglonglonglonglonglonglonglonglongclassname
19+
extends someobjectsomepropertysomeotherproperty.SomeClass {}
20+
21+
-------------------
22+
{ printWidth: 100 }
23+
-------------------
24+
export class longlonglonglonglonglonglonglonglonglonglongclassname
25+
extends someobjectsomepropertysomeotherproperty.SomeClass {}
26+
27+
export interface longlonglonglonglonglonglonglonglonglonglongclassname
28+
extends someobjectsomepropertysomeotherproperty.SomeClass {}
29+
30+
===================== End =====================

tasks/prettier_conformance/snapshots/prettier.ts.snap.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ts compatibility: 574/604 (95.03%)
1+
ts compatibility: 575/604 (95.20%)
22

33
# Failed
44

@@ -17,7 +17,6 @@ ts compatibility: 574/604 (95.03%)
1717
| typescript/chain-expression/test.ts | 💥 | 0.00% |
1818
| typescript/class/empty-method-body.ts | 💥 | 80.00% |
1919
| typescript/class/quoted-property.ts | 💥 | 66.67% |
20-
| typescript/class-and-interface/heritage-break/member-expression-like.ts | 💥 | 54.55% |
2120
| typescript/comments/mapped_types.ts | 💥 | 96.77% |
2221
| typescript/comments/method_types.ts | 💥 | 82.05% |
2322
| typescript/decorators-ts/angular.ts | 💥 | 87.50% |

0 commit comments

Comments
 (0)