@@ -45,6 +45,10 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
45
45
/// breaks and start/end contextual breaking tokens have been inserted.
46
46
private var preVisitedExprs = [ ExprSyntax] ( )
47
47
48
+ /// Lists the tokens that are the closing or right parens of a parenthesized expression (i.e. a
49
+ /// tuple expression with 1 element).
50
+ private var parenthesizedExprParens = Set < TokenSyntax > ( )
51
+
48
52
init ( configuration: Configuration , operatorContext: OperatorContext ) {
49
53
self . config = configuration
50
54
self . operatorContext = operatorContext
@@ -645,6 +649,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
645
649
// `stackedIndentationBehavior`).
646
650
after ( node. leftParen, tokens: . open)
647
651
before ( node. rightParen, tokens: . close)
652
+ parenthesizedExprParens. insert ( node. rightParen)
648
653
649
654
// When there's a comment inside of a parenthesized expression, we want to allow the comment
650
655
// to exist at the EOL with the left paren or on its own line. The contents are always
@@ -1348,9 +1353,16 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
1348
1353
node. colonMark,
1349
1354
tokens: . break( . close( mustBreak: false ) , size: 0 ) , . break( . open( kind: . continuation) ) , . open)
1350
1355
after ( node. colonMark, tokens: . space)
1351
- after (
1352
- node. secondChoice. lastToken,
1353
- tokens: . break( . close( mustBreak: false ) , size: 0 ) , . close, . close)
1356
+
1357
+ // When the ternary is wrapped in parens, absorb the closing paren into the ternary's group so
1358
+ // that it is glued to the last token of the ternary.
1359
+ let closeScopeToken : TokenSyntax ?
1360
+ if let parenExpr = outerMostEnclosingParenthesizedExpr ( from: Syntax ( node. secondChoice) ) {
1361
+ closeScopeToken = parenExpr. lastToken
1362
+ } else {
1363
+ closeScopeToken = node. secondChoice. lastToken
1364
+ }
1365
+ after ( closeScopeToken, tokens: . break( . close( mustBreak: false ) , size: 0 ) , . close, . close)
1354
1366
return . visitChildren
1355
1367
}
1356
1368
@@ -2866,6 +2878,24 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
2866
2878
}
2867
2879
}
2868
2880
2881
+ /// Returns the node for the outermost parenthesized expr (i.e. a single element tuple) that is
2882
+ /// ended at the given node. When the given node is not the last component of a parenthesized
2883
+ /// expression, this method returns nil.
2884
+ private func outerMostEnclosingParenthesizedExpr( from node: Syntax ) -> Syntax ? {
2885
+ guard let afterToken = node. lastToken? . nextToken, parenthesizedExprParens. contains ( afterToken)
2886
+ else {
2887
+ return nil
2888
+ }
2889
+ var parenthesizedExpr = afterToken. parent
2890
+ while let nextToken = parenthesizedExpr? . lastToken? . nextToken,
2891
+ parenthesizedExprParens. contains ( nextToken) ,
2892
+ let nextExpr = nextToken. parent
2893
+ {
2894
+ parenthesizedExpr = nextExpr
2895
+ }
2896
+ return parenthesizedExpr
2897
+ }
2898
+
2869
2899
/// Determines if indentation should be stacked around a subexpression to the right of the given
2870
2900
/// operator, and, if so, returns the node after which indentation stacking should be closed and
2871
2901
/// whether or not the continuation state should be reset as well.
@@ -2877,7 +2907,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
2877
2907
private func stackedIndentationBehavior(
2878
2908
after operatorExpr: ExprSyntax ? = nil ,
2879
2909
rhs: ExprSyntax
2880
- ) -> ( unindentingNode: ExprSyntax , shouldReset: Bool ) ? {
2910
+ ) -> ( unindentingNode: Syntax , shouldReset: Bool ) ? {
2881
2911
// Check for logical operators first, and if it's that kind of operator, stack indentation
2882
2912
// around the entire right-hand-side. We have to do this check before checking the RHS for
2883
2913
// parentheses because if the user writes something like `... && (foo) > bar || ...`, we don't
@@ -2892,21 +2922,35 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
2892
2922
precedence === operatorContext. precedenceGroup ( named: . logicalConjunction)
2893
2923
|| precedence === operatorContext. precedenceGroup ( named: . logicalDisjunction)
2894
2924
{
2895
- return ( unindentingNode: rhs, shouldReset: true )
2925
+ // When `rhs` side is the last sequence in an enclosing parenthesized expression, absorb the
2926
+ // paren into the right hand side by unindenting after the final closing paren. This glues
2927
+ // the paren to the last token of `rhs`.
2928
+ if let unindentingParenExpr = outerMostEnclosingParenthesizedExpr ( from: Syntax ( rhs) ) {
2929
+ return ( unindentingNode: unindentingParenExpr, shouldReset: true )
2930
+ }
2931
+ return ( unindentingNode: Syntax ( rhs) , shouldReset: true )
2896
2932
}
2897
2933
}
2898
2934
2899
2935
// If the right-hand-side is a ternary expression, stack indentation around the condition so
2900
2936
// that it is indented relative to the `?` and `:` tokens.
2901
2937
if let ternaryExpr = rhs. as ( TernaryExprSyntax . self) {
2902
- return ( unindentingNode: ternaryExpr. conditionExpression, shouldReset: false )
2938
+ // We don't try to absorb any parens in this case, because the condition of a ternary cannot
2939
+ // be grouped with any exprs outside of the condition.
2940
+ return ( unindentingNode: Syntax ( ternaryExpr. conditionExpression) , shouldReset: false )
2903
2941
}
2904
2942
2905
2943
// If the right-hand-side of the operator is or starts with a parenthesized expression, stack
2906
2944
// indentation around the operator and those parentheses. We don't need to reset here because
2907
2945
// the parentheses are sufficient to provide a visual indication of the nesting relationship.
2908
2946
if let parenthesizedExpr = parenthesizedLeftmostExpr ( of: rhs) {
2909
- return ( unindentingNode: ExprSyntax ( parenthesizedExpr) , shouldReset: false )
2947
+ // When `rhs` side is the last sequence in an enclosing parenthesized expression, absorb the
2948
+ // paren into the right hand side by unindenting after the final closing paren. This glues the
2949
+ // paren to the last token of `rhs`.
2950
+ if let unindentingParenExpr = outerMostEnclosingParenthesizedExpr ( from: Syntax ( rhs) ) {
2951
+ return ( unindentingNode: unindentingParenExpr, shouldReset: true )
2952
+ }
2953
+ return ( unindentingNode: Syntax ( parenthesizedExpr) , shouldReset: false )
2910
2954
}
2911
2955
2912
2956
// Otherwise, don't stack--use regular continuation breaks instead.
0 commit comments