@@ -6517,13 +6517,14 @@ struct UnionOrIntersectionType<'a, 'b> {
65176517}
65186518
65196519fn gen_union_or_intersection_type < ' a , ' b > ( node : UnionOrIntersectionType < ' a , ' b > , context : & mut Context < ' a > ) -> PrintItems {
6520- // todo: configuration for operator position
65216520 let mut items = PrintItems :: new ( ) ;
65226521 let force_use_new_lines = get_use_new_lines_for_nodes ( node. types , context. config . union_and_intersection_type_prefer_single_line , context) ;
65236522 let separator = if node. is_union { sc ! ( "|" ) } else { sc ! ( "&" ) } ;
6523+ let trailing_separator = if node. is_union { sc ! ( " |" ) } else { sc ! ( " &" ) } ;
65246524
65256525 let indent_width = context. config . indent_width ;
65266526 let prefer_hanging = context. config . union_and_intersection_type_prefer_hanging ;
6527+ let operator_position = context. config . union_and_intersection_type_operator_position ;
65276528 let is_parent_union_or_intersection = matches ! ( node. node. parent( ) . unwrap( ) . kind( ) , NodeKind :: TsUnionType | NodeKind :: TsIntersectionType ) ;
65286529 let multi_line_options = if !is_parent_union_or_intersection {
65296530 if use_surround_newlines ( node. node , context) {
@@ -6538,6 +6539,42 @@ fn gen_union_or_intersection_type<'a, 'b>(node: UnionOrIntersectionType<'a, 'b>,
65386539 |is_multi_line_or_hanging_ref| {
65396540 let is_multi_line_or_hanging = is_multi_line_or_hanging_ref. create_resolver ( ) ;
65406541 let types_count = node. types . len ( ) ;
6542+
6543+ // For each pair (between types[i-1] and types[i], i >= 1), decide whether the
6544+ // separator should be at the end of the previous line (SameLine) or at the
6545+ // start of the next line (NextLine). Index 0 is unused.
6546+ let pair_positions: Vec < OperatorPosition > = node
6547+ . types
6548+ . iter ( )
6549+ . enumerate ( )
6550+ . map ( |( i, type_node) | {
6551+ if i == 0 {
6552+ operator_position
6553+ } else {
6554+ resolve_pair_position ( & node, i, type_node, operator_position, separator. text , context)
6555+ }
6556+ } )
6557+ . collect ( ) ;
6558+
6559+ // Whether to emit the conditional leading separator on the first value when
6560+ // wrapping. NextLine = always (current behavior, leading-`|` hanging style).
6561+ // SameLine = never. Maintain = follow the source's leading-`|` presence.
6562+ let leading_first_when_multi_line = match operator_position {
6563+ OperatorPosition :: NextLine => true ,
6564+ OperatorPosition :: SameLine => false ,
6565+ OperatorPosition :: Maintain => {
6566+ if node. node . start_line_fast ( context. program ) == node. node . end_line_fast ( context. program ) {
6567+ true
6568+ } else {
6569+ node
6570+ . types
6571+ . first ( )
6572+ . and_then ( |t| context. token_finder . get_previous_token_if_operator ( & t. range ( ) , separator. text ) )
6573+ . is_some ( )
6574+ }
6575+ }
6576+ } ;
6577+
65416578 let mut generated_nodes = Vec :: new ( ) ;
65426579 for ( i, type_node) in node. types . iter ( ) . enumerate ( ) {
65436580 let ( allow_inline_multi_line, allow_inline_single_line) = {
@@ -6552,14 +6589,16 @@ fn gen_union_or_intersection_type<'a, 'b>(node: UnionOrIntersectionType<'a, 'b>,
65526589 if let Some ( separator_token) = separator_token {
65536590 items. extend ( gen_leading_comments ( & separator_token. range ( ) , context) ) ;
65546591 }
6555- if i == 0 && !is_parent_union_or_intersection {
6592+
6593+ let emit_leading_separator = i > 0 && matches ! ( pair_positions[ i] , OperatorPosition :: NextLine ) ;
6594+ if i == 0 && !is_parent_union_or_intersection && leading_first_when_multi_line {
65566595 items. push_condition ( if_true ( "separatorIfMultiLine" , is_multi_line_or_hanging. clone ( ) , {
65576596 // todo: .into() implementation for StringContainer
65586597 let mut items = PrintItems :: new ( ) ;
65596598 items. push_sc ( separator) ;
65606599 items
65616600 } ) ) ;
6562- } else if i > 0 {
6601+ } else if emit_leading_separator {
65636602 items. push_sc ( separator) ;
65646603 }
65656604
@@ -6579,6 +6618,11 @@ fn gen_union_or_intersection_type<'a, 'b>(node: UnionOrIntersectionType<'a, 'b>,
65796618 ) ) ;
65806619 items. extend ( gen_node ( type_node. into ( ) , context) ) ;
65816620
6621+ let next_is_same_line = i + 1 < types_count && matches ! ( pair_positions[ i + 1 ] , OperatorPosition :: SameLine ) ;
6622+ if next_is_same_line {
6623+ items. push_sc ( trailing_separator) ;
6624+ }
6625+
65826626 generated_nodes. push ( ir_helpers:: GeneratedValue {
65836627 items,
65846628 lines_span : None ,
@@ -6612,6 +6656,39 @@ fn gen_union_or_intersection_type<'a, 'b>(node: UnionOrIntersectionType<'a, 'b>,
66126656 _ => false ,
66136657 }
66146658 }
6659+
6660+ fn resolve_pair_position < ' a , ' b > (
6661+ node : & UnionOrIntersectionType < ' a , ' b > ,
6662+ i : usize ,
6663+ type_node : & TsType < ' a > ,
6664+ operator_position : OperatorPosition ,
6665+ separator_text : & str ,
6666+ context : & mut Context < ' a > ,
6667+ ) -> OperatorPosition {
6668+ match operator_position {
6669+ OperatorPosition :: NextLine => OperatorPosition :: NextLine ,
6670+ OperatorPosition :: SameLine => OperatorPosition :: SameLine ,
6671+ OperatorPosition :: Maintain => {
6672+ // When the whole union/intersection is on one source line, prefer dprint's
6673+ // default (NextLine) for the wrapping layout — matches how `Maintain` is
6674+ // handled for conditional expressions/types.
6675+ if node. node . start_line_fast ( context. program ) == node. node . end_line_fast ( context. program ) {
6676+ return OperatorPosition :: NextLine ;
6677+ }
6678+ match context. token_finder . get_previous_token_if_operator ( & type_node. range ( ) , separator_text) {
6679+ Some ( sep_token) => {
6680+ let prev_end_line = node. types [ i - 1 ] . end_line_fast ( context. program ) ;
6681+ if prev_end_line == sep_token. start_line_fast ( context. program ) {
6682+ OperatorPosition :: SameLine
6683+ } else {
6684+ OperatorPosition :: NextLine
6685+ }
6686+ }
6687+ None => OperatorPosition :: NextLine ,
6688+ }
6689+ }
6690+ }
6691+ }
66156692}
66166693
66176694/* comments */
0 commit comments