@@ -448,9 +448,11 @@ extension Formatter {
448448 }
449449 }
450450
451+ let hasLineBreakAfterOpeningParen = nextToken ( after: i, where: { !$0. isComment } ) ? . isLinebreak == true
452+
451453 if closingParenOnSameLine {
452454 removeLinebreakBeforeEndOfScope ( at: & endOfScope)
453- } else if insertLinebreakAfterOpeningParen {
455+ } else if hasLineBreakAfterOpeningParen {
454456 // Insert linebreak before closing paren
455457 if let lastIndex = self . index ( of: . nonSpace, before: endOfScope) {
456458 endOfScope += insertSpace ( indent, at: lastIndex + 1 )
@@ -522,6 +524,98 @@ extension Formatter {
522524 )
523525 }
524526
527+ // Wrap nested structures like typealiases and ternary operators first since this may
528+ // prevent the need to do wrap the child expressions.
529+
530+ // -- wraptypealiases
531+ forEach ( . keyword( " typealias " ) ) { typealiasIndex, _ in
532+ guard options. wrapTypealiases == . beforeFirst || options. wrapTypealiases == . afterFirst,
533+ let ( equalsIndex, andTokenIndices, lastIdentifierIndex) = parseProtocolCompositionTypealias ( at: typealiasIndex)
534+ else { return }
535+
536+ // Decide which indices to wrap at
537+ // - We always wrap at each `&`
538+ // - For `beforeFirst`, we also wrap before the `=`
539+ let wrapIndices : [ Int ]
540+ switch options. wrapTypealiases {
541+ case . afterFirst:
542+ wrapIndices = andTokenIndices
543+ case . beforeFirst:
544+ wrapIndices = [ equalsIndex] + andTokenIndices
545+ case . default, . disabled, . preserve:
546+ return
547+ }
548+
549+ let didWrap = wrapMultilineStatement (
550+ startIndex: typealiasIndex,
551+ delimiterIndices: wrapIndices,
552+ endIndex: lastIdentifierIndex
553+ )
554+
555+ guard didWrap else { return }
556+
557+ // If we're using `afterFirst` and there was unexpectedly a linebreak
558+ // between the `typealias` and the `=`, we need to remove it
559+ let rangeBetweenTypealiasAndEquals = ( typealiasIndex + 1 ) ..< equalsIndex
560+ if options. wrapTypealiases == . afterFirst,
561+ let linebreakIndex = rangeBetweenTypealiasAndEquals. first ( where: { tokens [ $0] . isLinebreak } )
562+ {
563+ removeToken ( at: linebreakIndex)
564+ if tokens [ linebreakIndex] . isSpace, tokens [ linebreakIndex] != . space( " " ) {
565+ replaceToken ( at: linebreakIndex, with: . space( " " ) )
566+ }
567+ }
568+ }
569+
570+ // --wrapternary
571+ forEach ( . operator( " ? " , . infix) ) { conditionIndex, _ in
572+ guard options. wrapTernaryOperators != . default,
573+ let expressionStartIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: conditionIndex) ,
574+ !isInStringLiteralWithWrappingDisabled( at: conditionIndex)
575+ else { return }
576+
577+ // Find the : operator that separates the true and false branches
578+ // of this ternary operator
579+ // - You can have nested ternary operators, so the immediate-next colon
580+ // is not necessarily the colon of _this_ ternary operator.
581+ // - To track nested ternary operators, we maintain a count of
582+ // the unterminated `?` tokens that we've seen.
583+ // - This ternary's colon token is the first colon we find
584+ // where there isn't an unterminated `?`.
585+ var unterimatedTernaryCount = 0
586+ var currentIndex = conditionIndex + 1
587+ var foundColonIndex : Int ?
588+
589+ while foundColonIndex == nil ,
590+ currentIndex < tokens. count
591+ {
592+ switch tokens [ currentIndex] {
593+ case . operator( " ? " , . infix) :
594+ unterimatedTernaryCount += 1
595+ case . operator( " : " , . infix) :
596+ if unterimatedTernaryCount == 0 {
597+ foundColonIndex = currentIndex
598+ } else {
599+ unterimatedTernaryCount -= 1
600+ }
601+ default :
602+ break
603+ }
604+
605+ currentIndex += 1
606+ }
607+
608+ guard let colonIndex = foundColonIndex,
609+ let endOfElseExpression = endOfExpression ( at: colonIndex, upTo: [ ] )
610+ else { return }
611+
612+ wrapMultilineStatement (
613+ startIndex: expressionStartIndex,
614+ delimiterIndices: [ conditionIndex, colonIndex] ,
615+ endIndex: endOfElseExpression
616+ )
617+ }
618+
525619 var lastIndex = - 1
526620 forEachToken ( onlyWhereEnabled: false ) { i, token in
527621 guard case let . startOfScope( string) = token else {
@@ -800,95 +894,6 @@ extension Formatter {
800894
801895 return true
802896 }
803-
804- // -- wraptypealiases
805- forEach ( . keyword( " typealias " ) ) { typealiasIndex, _ in
806- guard options. wrapTypealiases == . beforeFirst || options. wrapTypealiases == . afterFirst,
807- let ( equalsIndex, andTokenIndices, lastIdentifierIndex) = parseProtocolCompositionTypealias ( at: typealiasIndex)
808- else { return }
809-
810- // Decide which indices to wrap at
811- // - We always wrap at each `&`
812- // - For `beforeFirst`, we also wrap before the `=`
813- let wrapIndices : [ Int ]
814- switch options. wrapTypealiases {
815- case . afterFirst:
816- wrapIndices = andTokenIndices
817- case . beforeFirst:
818- wrapIndices = [ equalsIndex] + andTokenIndices
819- case . default, . disabled, . preserve:
820- return
821- }
822-
823- let didWrap = wrapMultilineStatement (
824- startIndex: typealiasIndex,
825- delimiterIndices: wrapIndices,
826- endIndex: lastIdentifierIndex
827- )
828-
829- guard didWrap else { return }
830-
831- // If we're using `afterFirst` and there was unexpectedly a linebreak
832- // between the `typealias` and the `=`, we need to remove it
833- let rangeBetweenTypealiasAndEquals = ( typealiasIndex + 1 ) ..< equalsIndex
834- if options. wrapTypealiases == . afterFirst,
835- let linebreakIndex = rangeBetweenTypealiasAndEquals. first ( where: { tokens [ $0] . isLinebreak } )
836- {
837- removeToken ( at: linebreakIndex)
838- if tokens [ linebreakIndex] . isSpace, tokens [ linebreakIndex] != . space( " " ) {
839- replaceToken ( at: linebreakIndex, with: . space( " " ) )
840- }
841- }
842- }
843-
844- // --wrapternary
845- forEach ( . operator( " ? " , . infix) ) { conditionIndex, _ in
846- guard options. wrapTernaryOperators != . default,
847- let expressionStartIndex = index ( of: . nonSpaceOrCommentOrLinebreak, before: conditionIndex) ,
848- !isInStringLiteralWithWrappingDisabled( at: conditionIndex)
849- else { return }
850-
851- // Find the : operator that separates the true and false branches
852- // of this ternary operator
853- // - You can have nested ternary operators, so the immediate-next colon
854- // is not necessarily the colon of _this_ ternary operator.
855- // - To track nested ternary operators, we maintain a count of
856- // the unterminated `?` tokens that we've seen.
857- // - This ternary's colon token is the first colon we find
858- // where there isn't an unterminated `?`.
859- var unterimatedTernaryCount = 0
860- var currentIndex = conditionIndex + 1
861- var foundColonIndex : Int ?
862-
863- while foundColonIndex == nil ,
864- currentIndex < tokens. count
865- {
866- switch tokens [ currentIndex] {
867- case . operator( " ? " , . infix) :
868- unterimatedTernaryCount += 1
869- case . operator( " : " , . infix) :
870- if unterimatedTernaryCount == 0 {
871- foundColonIndex = currentIndex
872- } else {
873- unterimatedTernaryCount -= 1
874- }
875- default :
876- break
877- }
878-
879- currentIndex += 1
880- }
881-
882- guard let colonIndex = foundColonIndex,
883- let endOfElseExpression = endOfExpression ( at: colonIndex, upTo: [ ] )
884- else { return }
885-
886- wrapMultilineStatement (
887- startIndex: expressionStartIndex,
888- delimiterIndices: [ conditionIndex, colonIndex] ,
889- endIndex: endOfElseExpression
890- )
891- }
892897 }
893898
894899 /// Returns the index where the `wrap` rule should add the next linebreak in the line at the selected index.
0 commit comments