11use oxc_ast:: ast:: * ;
2- use oxc_span:: GetSpan ;
2+ use oxc_span:: { GetSpan , Span } ;
33
44use crate :: {
55 ast_nodes:: { AstNode , AstNodes } ,
@@ -13,6 +13,7 @@ use crate::{
1313 utils:: {
1414 assignment_like:: AssignmentLikeLayout , expression:: ExpressionLeftSide ,
1515 format_node_without_trailing_comments:: FormatNodeWithoutTrailingComments ,
16+ suppressed:: FormatSuppressedNode ,
1617 } ,
1718 write,
1819} ;
@@ -143,20 +144,10 @@ impl<'a, 'b> FormatJsArrowFunctionExpression<'a, 'b> {
143144 let arrow_expression = arrow. get_expression ( ) ;
144145
145146 if let Some ( Expression :: SequenceExpression ( sequence) ) = arrow_expression {
146- return if f. context ( ) . comments ( ) . has_comment_before ( sequence. span ( ) . start ) {
147- write ! (
148- f,
149- [ group( & format_args!(
150- formatted_signature,
151- group( & format_args!( indent( & format_args!(
152- hard_line_break( ) ,
153- format_leading_comments( sequence. span( ) ) ,
154- token( "(" ) ,
155- format_body,
156- token( ")" )
157- ) ) ) )
158- ) ) ]
159- ) ;
147+ return if let Some ( format_sequence) =
148+ format_sequence_with_leading_comment ( sequence. span ( ) , & format_body, f)
149+ {
150+ write ! ( f, [ group( & format_args!( formatted_signature, format_sequence) ) ] ) ;
160151 } else {
161152 write ! (
162153 f,
@@ -564,17 +555,10 @@ impl<'a> Format<'a> for ArrowChain<'a, '_> {
564555 // Ensure that the parens of sequence expressions end up on their own line if the
565556 // body breaks
566557 if let Some ( Expression :: SequenceExpression ( sequence) ) = tail. get_expression ( ) {
567- if f. context ( ) . comments ( ) . has_comment_before ( sequence. span ( ) . start ) {
568- write ! (
569- f,
570- [ group( & format_args!( indent( & format_args!(
571- hard_line_break( ) ,
572- format_leading_comments( sequence. span( ) ) ,
573- token( "(" ) ,
574- format_tail_body,
575- token( ")" )
576- ) ) ) ) ]
577- ) ;
558+ if let Some ( format_sequence) =
559+ format_sequence_with_leading_comment ( sequence. span ( ) , & format_tail_body, f)
560+ {
561+ write ! ( f, format_sequence) ;
578562 } else {
579563 write ! (
580564 f,
@@ -761,3 +745,44 @@ impl<'a> Format<'a> for FormatMaybeCachedFunctionBody<'a, '_> {
761745 FormatContentWithCacheMode :: new ( self . body . span , content, self . mode ) . fmt ( f) ;
762746 }
763747}
748+
749+ /// Format a sequence expression in an arrow function body that has a leading comment.
750+ ///
751+ /// When an arrow function body is a sequence expression (e.g., `() => (a, b, c)`) and has
752+ /// a leading comment, special formatting is needed to place the comment correctly:
753+ ///
754+ /// ```js
755+ /// const f = () =>
756+ /// // comment
757+ /// (a, b, c);
758+ /// ```
759+ ///
760+ /// Returns `Some(formatter)` if the sequence has a leading comment, `None` otherwise.
761+ /// When `None`, the caller should use normal formatting with `soft_block_indent`.
762+ ///
763+ /// Handles `oxfmt-ignore` by preserving original source text when suppressed.
764+ fn format_sequence_with_leading_comment < ' a , ' b > (
765+ sequence_span : Span ,
766+ format_body : & ' b impl Format < ' a > ,
767+ f : & Formatter < ' _ , ' a > ,
768+ ) -> Option < impl Format < ' a > + ' b > {
769+ if !f. comments ( ) . has_comment_before ( sequence_span. start ) {
770+ return None ;
771+ }
772+
773+ let is_suppressed = f. comments ( ) . is_suppressed ( sequence_span. start ) ;
774+
775+ let format_sequence = format_with ( move |f| {
776+ write ! ( f, [ format_leading_comments( sequence_span) , "(" ] ) ;
777+ if is_suppressed {
778+ write ! ( f, FormatSuppressedNode ( sequence_span) ) ;
779+ } else {
780+ write ! ( f, format_body) ;
781+ }
782+ write ! ( f, [ ")" ] ) ;
783+ } ) ;
784+
785+ Some ( format_with ( move |f| {
786+ write ! ( f, group( & indent( & format_args!( hard_line_break( ) , format_sequence) ) ) ) ;
787+ } ) )
788+ }
0 commit comments