Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/oxc_formatter/src/generated/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3807,9 +3807,19 @@ impl<'a> Format<'a> for AstNode<'a, TSUnionType<'a>> {
impl<'a> Format<'a> for AstNode<'a, TSIntersectionType<'a>> {
fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
let is_suppressed = f.comments().is_suppressed(self.span().start);
if !is_suppressed && format_type_cast_comment_node(self, false, f)? {
return Ok(());
}
self.format_leading_comments(f)?;
let needs_parentheses = self.needs_parentheses(f);
if needs_parentheses {
"(".fmt(f)?;
}
let result =
if is_suppressed { FormatSuppressedNode(self.span()).fmt(f) } else { self.write(f) };
if needs_parentheses {
")".fmt(f)?;
}
self.format_trailing_comments(f)?;
result
}
Expand Down
108 changes: 108 additions & 0 deletions crates/oxc_formatter/src/write/intersection_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_span::GetSpan;

use crate::{
format_args,
formatter::{FormatResult, Formatter, prelude::*},
generated::ast_nodes::{AstNode, AstNodes},
parentheses::NeedsParentheses,
write,
write::FormatWrite,
};

impl<'a> FormatWrite<'a> for AstNode<'a, TSIntersectionType<'a>> {
fn write(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
let types = self.types();

if types.len() == 1 {
return write!(f, self.types().first());
}

let content = format_with(|f| {
if self.needs_parentheses(f) {
return write!(
f,
[
indent(&format_once(|f| format_intersection_types(types, f))),
soft_line_break()
]
);
}

let is_inside_complex_tuple_type = match self.parent {
AstNodes::TSTupleType(tuple) => tuple.element_types().len() > 1,
_ => false,
};

if is_inside_complex_tuple_type {
write!(
f,
[
indent(&format_args!(
if_group_breaks(&format_args!(text("("), soft_line_break())),
format_once(|f| format_intersection_types(types, f))
)),
soft_line_break(),
if_group_breaks(&text(")"))
]
)
} else {
format_intersection_types(types, f)
}
});

write!(f, [group(&content)])
}
}

/// Check if a TSType is object-like (object literal, mapped type, etc.)
fn is_object_like_type(ty: &TSType) -> bool {
matches!(ty, TSType::TSTypeLiteral(_) | TSType::TSMappedType(_))
}

// [Prettier applies]: https://github.com/prettier/prettier/blob/cd3e530c2e51fb8296c0fb7738a9afdd3a3a4410/src/language-js/print/type-annotation.js#L93-L120
fn format_intersection_types<'a>(
node: &AstNode<'a, Vec<'a, TSType<'a>>>,
f: &mut Formatter<'_, 'a>,
) -> FormatResult<()> {
let last_index = node.len().saturating_sub(1);
let mut is_prev_object_like = false;
let mut is_chain_indented = false;

for (index, item) in node.iter().enumerate() {
let is_object_like = is_object_like_type(item.as_ref());

// always inline first element
if index == 0 {
write!(f, item)?;
} else {
// If no object is involved, go to the next line if it breaks
if !is_prev_object_like && !is_object_like {
write!(f, [indent(&format_args!(soft_line_break_or_space(), item))])?;
} else {
write!(f, space())?;

if !is_prev_object_like || !is_object_like {
// indent if we move from object to non-object or vice versa, otherwise keep inline
is_chain_indented = index > 1;
}

if is_chain_indented {
write!(f, [indent(&item)])?;
} else {
write!(f, item)?;
}
}
}

// Add separator if not the last element
if index < last_index {
write!(f, [space(), "&"])?;
}

is_prev_object_like = is_object_like;
}

Ok(())
}
22 changes: 1 addition & 21 deletions crates/oxc_formatter/src/write/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod export_declarations;
mod function;
mod import_declaration;
mod import_expression;
mod intersection_type;
mod jsx;
mod member_expression;
mod object_like;
Expand Down Expand Up @@ -1210,27 +1211,6 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSConditionalType<'a>> {
}
}

impl<'a> FormatWrite<'a> for AstNode<'a, TSIntersectionType<'a>> {
fn write(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
let mut types = self.types().iter();
let needs_parentheses = self.needs_parentheses(f);
if needs_parentheses {
write!(f, "(")?;
}
if let Some(item) = types.next() {
write!(f, item)?;

for item in types {
write!(f, [" & ", item])?;
}
}
if needs_parentheses {
write!(f, ")")?;
}
Ok(())
}
}

impl<'a> FormatWrite<'a> for AstNode<'a, TSParenthesizedType<'a>> {
fn write(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
write!(f, ["(", self.type_annotation(), ")"])
Expand Down
2 changes: 1 addition & 1 deletion tasks/ast_tools/src/generators/formatter/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const AST_NODE_WITHOUT_PRINTING_COMMENTS_LIST: &[&str] = &[
];

const AST_NODE_NEEDS_PARENTHESES: &[&str] =
&["TSTypeAssertion", "TSInferType", "TSConditionalType", "TSUnionType"];
&["TSTypeAssertion", "TSInferType", "TSConditionalType", "TSUnionType", "TSIntersectionType"];

const NEEDS_IMPLEMENTING_FMT_WITH_OPTIONS: phf::Map<&'static str, &'static str> = phf::phf_map! {
"ArrowFunctionExpression" => "FormatJsArrowFunctionExpressionOptions",
Expand Down
4 changes: 1 addition & 3 deletions tasks/coverage/snapshots/formatter_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ commit: 261630d6

formatter_typescript Summary:
AST Parsed : 8816/8816 (100.00%)
Positive Passed: 8788/8816 (99.68%)
Positive Passed: 8789/8816 (99.69%)
Mismatch: tasks/coverage/typescript/tests/cases/compiler/amdLikeInputDeclarationEmit.ts

Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/arrayFromAsync.ts
Expand All @@ -15,8 +15,6 @@ Mismatch: tasks/coverage/typescript/tests/cases/compiler/complexNarrowingWithAny

Mismatch: tasks/coverage/typescript/tests/cases/compiler/declarationEmitCastReusesTypeNode4.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/declarationEmitRecursiveConditionalAliasPreserved.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/declarationEmitShadowingInferNotRenamed.ts

Mismatch: tasks/coverage/typescript/tests/cases/compiler/jsxNamespaceGlobalReexport.tsx
Expand Down
13 changes: 5 additions & 8 deletions tasks/prettier_conformance/snapshots/prettier.ts.snap.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ts compatibility: 414/573 (72.25%)
ts compatibility: 417/573 (72.77%)

# Failed

Expand All @@ -17,9 +17,8 @@ ts compatibility: 414/573 (72.25%)
| typescript/arrow/16067.ts | 💥💥 | 95.92% |
| typescript/arrow/comments.ts | 💥✨ | 44.44% |
| typescript/as/as.ts | 💥 | 89.06% |
| typescript/as/assignment2.ts | 💥 | 94.12% |
| typescript/as/expression-statement.ts | 💥 | 75.00% |
| typescript/assignment/issue-10846.ts | 💥 | 60.00% |
| typescript/assignment/issue-10846.ts | 💥 | 57.14% |
| typescript/assignment/issue-10848.tsx | 💥 | 52.12% |
| typescript/assignment/issue-10850.ts | 💥 | 50.00% |
| typescript/cast/generic-cast.ts | 💥 | 39.24% |
Expand Down Expand Up @@ -91,18 +90,18 @@ ts compatibility: 414/573 (72.25%)
| typescript/export/export.ts | 💥 | 85.71% |
| typescript/function-type/consistent.ts | 💥 | 70.83% |
| typescript/function-type/type-annotation.ts | 💥 | 0.00% |
| typescript/generic/arrow-return-type.ts | 💥 | 80.00% |
| typescript/generic/arrow-return-type.ts | 💥 | 79.25% |
| typescript/generic/issue-6899.ts | 💥 | 21.05% |
| typescript/generic/object-method.ts | 💥 | 72.73% |
| typescript/index-signature/static.ts | 💥 | 66.67% |
| typescript/infer-extends/basic.ts | 💥 | 71.43% |
| typescript/interface/comments-generic.ts | 💥💥 | 30.00% |
| typescript/interface/ignore.ts | 💥💥 | 88.26% |
| typescript/interface/long-type-parameters/long-type-parameters.ts | 💥💥 | 45.00% |
| typescript/interface/long-type-parameters/long-type-parameters.ts | 💥💥 | 43.05% |
| typescript/interface2/comments-declare.ts | 💥 | 66.67% |
| typescript/interface2/comments.ts | 💥 | 78.87% |
| typescript/interface2/break/break.ts | 💥💥💥 | 80.23% |
| typescript/intersection/intersection-parens.ts | 💥💥 | 59.14% |
| typescript/intersection/intersection-parens.ts | 💥💥 | 72.25% |
| typescript/intersection/type-arguments.ts | 💥💥 | 46.67% |
| typescript/intersection/consistent-with-flow/intersection-parens.ts | 💥 | 69.77% |
| typescript/key-remapping-in-mapped-types/key-remapping.ts | 💥 | 23.53% |
Expand Down Expand Up @@ -130,7 +129,6 @@ ts compatibility: 414/573 (72.25%)
| typescript/prettier-ignore/prettier-ignore-parenthesized-type.ts | 💥 | 0.00% |
| typescript/rest-type/complex.ts | 💥 | 0.00% |
| typescript/rest-type/infer-type.ts | 💥 | 80.00% |
| typescript/satisfies-operators/assignment.ts | 💥💥 | 90.91% |
| typescript/satisfies-operators/expression-statement.ts | 💥💥 | 78.38% |
| typescript/satisfies-operators/lhs.ts | 💥✨ | 35.00% |
| typescript/template-literal-types/template-literal-types.ts | 💥 | 80.00% |
Expand All @@ -156,7 +154,6 @@ ts compatibility: 414/573 (72.25%)
| typescript/typeparams/long-function-arg.ts | 💥 | 76.92% |
| typescript/typeparams/empty-parameters-with-arrow-function/issue-13817.ts | 💥 | 66.67% |
| typescript/typeparams/trailing-comma/type-paramters.ts | 💥💥💥 | 28.57% |
| typescript/union/comments.ts | 💥 | 84.21% |
| typescript/union/inlining.ts | 💥 | 77.37% |
| typescript/union/union-parens.ts | 💥 | 92.59% |
| typescript/union/with-type-params.ts | 💥 | 37.50% |
Expand Down
Loading