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
12 changes: 5 additions & 7 deletions crates/oxc_formatter/src/ast_nodes/generated/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4202,13 +4202,11 @@ impl<'a> Format<'a> for AstNode<'a, TSTypeAliasDeclaration<'a>> {
impl<'a> Format<'a> for AstNode<'a, TSClassImplements<'a>> {
fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
let is_suppressed = f.comments().is_suppressed(self.span().start);
if is_suppressed {
self.format_leading_comments(f)?;
FormatSuppressedNode(self.span()).fmt(f)?;
self.format_trailing_comments(f)
} else {
self.write(f)
}
self.format_leading_comments(f)?;
let result =
if is_suppressed { FormatSuppressedNode(self.span()).fmt(f) } else { self.write(f) };
self.format_trailing_comments(f)?;
result
}
}

Expand Down
32 changes: 21 additions & 11 deletions crates/oxc_formatter/src/formatter/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,6 @@ impl<'a> Comments<'a> {
let Some(following_span) = following_span else {
// Find dangling comments at the end of the enclosing node
let comments = self.comments_before(enclosing_span.end);

let mut start = preceding_span.end;
for (idx, comment) in comments.iter().enumerate() {
// Comments inside the preceding node, which should be printed without checking
Expand All @@ -333,30 +332,41 @@ impl<'a> Comments<'a> {
};

let mut comment_index = 0;
while let Some(comment) = comments.get(comment_index) {
// Check if the comment is before the following node's span
if comment.span.end > following_span.start {
break;
}
let mut type_cast_comment = None;

if matches!(comment.content, CommentContent::Jsdoc)
&& self.is_type_cast_comment(comment)
while let Some(comment) = comments.get(comment_index) {
// Stop if the comment:
// 1. is over the following node
// 2. is after the enclosing node, which means the comment should be printed in the parent node.
if comment.span.end > following_span.start
|| (comment.span.end > enclosing_span.end && enclosing_span != preceding_span)
{
break;
}

if self.is_own_line_comment(comment) {
// Own line comments are typically leading comments for the next node
if following_span.start > enclosing_span.end && comment.span.end <= enclosing_span.end {
// Do nothing; this comment is inside the enclosing node, and the following node is outside the enclosing node.
// So it must be a trailing comment, continue checking the next comment.
} else if self.is_type_cast_comment(comment) {
// `A || /* @type {Number} */ (B)`:
// ^^^^^^^^^^^^^^^^^^^^^^^^
// Type cast comments should always be treated as leading comment to the following node
type_cast_comment = Some(comment);
break;
} else if self.is_own_line_comment(comment) {
// Own-line comments should be treated as leading comments to the following node
break;
} else if self.is_end_of_line_comment(comment) {
//End-of-line comments are always trailing comments to the preceding node.
return &comments[..=comment_index];
}

comment_index += 1;
}

// Find the first comment (from the end) that has non-whitespace/non-paren content after it
let mut gap_end = following_span.start;
let mut gap_end = type_cast_comment.map_or(following_span.start, |c| c.span.start);

for (idx, comment) in comments[..comment_index].iter().enumerate().rev() {
if source_text.all_bytes_match(comment.span.end, gap_end, |b| {
b.is_ascii_whitespace() || b == b'('
Expand Down
35 changes: 13 additions & 22 deletions crates/oxc_formatter/src/write/binary_like_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use crate::{
ast_nodes::{AstNode, AstNodes},
formatter::{FormatResult, Formatter, trivia::FormatTrailingComments},
parentheses::NeedsParentheses,
utils::format_node_without_trailing_comments::FormatNodeWithoutTrailingComments,
};

use crate::{format_args, formatter::prelude::*, write};
Expand Down Expand Up @@ -294,8 +293,6 @@ enum BinaryLeftOrRightSide<'a, 'b> {
parent: BinaryLikeExpression<'a, 'b>,
/// Is the parent the condition of a `if` / `while` / `do-while` / `for` statement?
inside_condition: bool,
/// It is the root of the expression.
root: bool,
},
}

Expand All @@ -306,7 +303,6 @@ impl<'a> Format<'a> for BinaryLeftOrRightSide<'a, '_> {
Self::Right {
parent: binary_like_expression,
inside_condition: inside_parenthesis,
root,
} => {
let mut binary_like_expression = *binary_like_expression;
// // It's only possible to suppress the formatting of the whole binary expression formatting OR
Expand Down Expand Up @@ -401,16 +397,7 @@ impl<'a> Format<'a> for BinaryLeftOrRightSide<'a, '_> {
write!(f, [soft_line_break_or_space()])?;
}

if *root {
write!(f, FormatNodeWithoutTrailingComments(right))?;
let comments = f
.context()
.comments()
.comments_before(binary_like_expression.span().end);
write!(f, FormatTrailingComments::Comments(comments))
} else {
write!(f, right)
}
write!(f, right)
});

// Doesn't match prettier that only distinguishes between logical and binary
Expand All @@ -425,6 +412,16 @@ impl<'a> Format<'a> for BinaryLeftOrRightSide<'a, '_> {
right.as_ast_nodes(),
) || (*inside_parenthesis && logical_operator.is_some()));

match binary_like_expression.left().as_ast_nodes() {
AstNodes::LogicalExpression(logical) => {
logical.format_trailing_comments(f)?;
}
AstNodes::BinaryExpression(binary) => {
binary.format_trailing_comments(f)?;
}
_ => {}
}

if should_group {
// `left` side has printed before `right` side, so that trailing comments of `left` side has been printed,
// so we need to find if there are any printed comments that are after the `left` side and it is line comment.
Expand Down Expand Up @@ -489,7 +486,6 @@ fn split_into_left_and_right_sides<'a, 'b>(
inside_condition: bool,
) -> Vec<BinaryLeftOrRightSide<'a, 'b>> {
fn split_into_left_and_right_sides_inner<'a, 'b>(
is_root: bool,
binary: BinaryLikeExpression<'a, 'b>,
inside_condition: bool,
items: &mut Vec<BinaryLeftOrRightSide<'a, 'b>>,
Expand All @@ -500,7 +496,6 @@ fn split_into_left_and_right_sides<'a, 'b>(
// We can flatten the left hand side, so we need to check if we have a nested binary expression
// that we can flatten.
split_into_left_and_right_sides_inner(
false,
// SAFETY: `left` is guaranteed to be a valid binary like expression in `can_flatten()`.
BinaryLikeExpression::try_from(left).unwrap(),
inside_condition,
Expand All @@ -510,19 +505,15 @@ fn split_into_left_and_right_sides<'a, 'b>(
items.push(BinaryLeftOrRightSide::Left { parent: binary });
}

items.push(BinaryLeftOrRightSide::Right {
parent: binary,
inside_condition,
root: is_root,
});
items.push(BinaryLeftOrRightSide::Right { parent: binary, inside_condition });
}

// Stores the left and right parts of the binary expression in sequence (rather than nested as they
// appear in the tree).
// `with_capacity(2)` because we expect at least 2 items (left and right).
let mut items = Vec::with_capacity(2);

split_into_left_and_right_sides_inner(true, binary, inside_condition, &mut items);
split_into_left_and_right_sides_inner(binary, inside_condition, &mut items);

items
}
Expand Down
24 changes: 16 additions & 8 deletions crates/oxc_formatter/src/write/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,22 @@ impl<'a> Format<'a> for AstNode<'a, Vec<'a, TSClassImplements<'a>>> {
group(&indent(&format_args!(
soft_line_break_or_space(),
format_once(|f| {
// the grouping will be applied by the parent
f.join_with(&soft_line_break_or_space())
.entries_with_trailing_separator(
self.iter(),
",",
TrailingSeparator::Disallowed,
)
.finish()
let last_index = self.len().saturating_sub(1);
let mut joiner = f.join_with(soft_line_break_or_space());

for (i, heritage) in FormatSeparatedIter::new(self.into_iter(), ",")
.with_trailing_separator(TrailingSeparator::Disallowed)
.enumerate()
{
if i == last_index {
// The trailing comments of the last heritage should be printed inside the class declaration
joiner.entry(&FormatNodeWithoutTrailingComments(&heritage));
} else {
joiner.entry(&heritage);
}
}

joiner.finish()
})
)))
]
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_formatter/src/write/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1628,6 +1628,7 @@ impl<'a> Format<'a> for AstNode<'a, Vec<'a, TSInterfaceHeritage<'a>>> {
.enumerate()
{
if i == last_index {
// The trailing comments of the last heritage should be printed inside the interface declaration
joiner.entry(&FormatNodeWithoutTrailingComments(&heritage));
} else {
joiner.entry(&heritage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ source: crates/oxc_formatter/tests/fixtures/mod.rs
(a) === "call" ||
/** @type {Identifier} */
(b) === "bind"
// ^^^^^^^^^^^^^^ No need to wrap with parentheses here because the type cast node is already wrapped with parentheses.
) &&
// ^^^^^^^^^^^^^^ No need to wrap with parentheses here because the type cast node is already wrapped with parentheses.
right;

/** @type {Number} */ (a + b)();
Expand Down
1 change: 0 additions & 1 deletion tasks/ast_tools/src/generators/formatter/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const AST_NODE_WITHOUT_PRINTING_COMMENTS_LIST: &[&str] = &[
// Manually prints it because class's decorators can be appears before `export class Cls {}`.
"ExportNamedDeclaration",
"ExportDefaultDeclaration",
"TSClassImplements",
//
"JSXElement",
"JSXFragment",
Expand Down
Loading