@@ -522,6 +522,7 @@ class KotlinInputAstVisitor(
522
522
}
523
523
val argsIndentElse = if (index == parts.size - 1 ) ZERO else expressionBreakIndent
524
524
val lambdaIndentElse = if (isTrailingLambda) expressionBreakNegativeIndent else ZERO
525
+
525
526
// emit `(1, 2) { it }` from `doIt(1, 2) { it }`
526
527
visitCallElement(
527
528
null ,
@@ -751,9 +752,14 @@ class KotlinInputAstVisitor(
751
752
builder.token(" )" )
752
753
}
753
754
}
755
+ val hasTrailingComma = argumentList?.trailingComma != null
754
756
if (lambdaArguments.isNotEmpty()) {
755
757
builder.space()
756
- builder.block(lambdaIndent) { lambdaArguments.forEach { visit(it) } }
758
+ builder.block(lambdaIndent) {
759
+ lambdaArguments.forEach {
760
+ visitArgumentInternal(it, forceBreakLambdaBody = hasTrailingComma)
761
+ }
762
+ }
757
763
}
758
764
}
759
765
}
@@ -783,12 +789,38 @@ class KotlinInputAstVisitor(
783
789
784
790
/* * Example `{ 1 + 1 }` (as lambda) or `{ (x, y) -> x + y }` */
785
791
override fun visitLambdaExpression (lambdaExpression : KtLambdaExpression ) {
786
- visitLambdaExpression (lambdaExpression, null as BreakTag ? )
792
+ visitLambdaExpressionInternal (lambdaExpression, brokeBeforeBrace = null , forceBreakBody = false )
787
793
}
788
794
789
- private fun visitLambdaExpression (
795
+ /* *
796
+ * The internal version of [visitLambdaExpression].
797
+ *
798
+ * @param brokeBeforeBrace used for tracking if a break was taken right before the lambda
799
+ * expression. Useful for scoping functions where we want good looking indentation. For example,
800
+ * here we have correct indentation before `bar()` and `car()` because we can detect the break
801
+ * after the equals:
802
+ * ```
803
+ * fun foo() =
804
+ * coroutineScope { x ->
805
+ * bar()
806
+ * car()
807
+ * }
808
+ * ```
809
+ * @param forceBreakBody if true, forces the lambda to be multi-line. Useful for call expressions
810
+ * where it would look weird for the lambda to be on one-line. For example, here we avoid
811
+ * one-lining `{ x = 0 }` since the parameters have a trailing comma:
812
+ * ```
813
+ * foo.bar(
814
+ * trailingComma,
815
+ * ) {
816
+ * x = 0
817
+ * }
818
+ * ```
819
+ */
820
+ private fun visitLambdaExpressionInternal (
790
821
lambdaExpression : KtLambdaExpression ,
791
822
brokeBeforeBrace : BreakTag ? ,
823
+ forceBreakBody : Boolean ,
792
824
) {
793
825
builder.sync(lambdaExpression)
794
826
@@ -838,6 +870,10 @@ class KotlinInputAstVisitor(
838
870
builder.breakOp(Doc .FillMode .UNIFIED , " " , bracePlusZeroIndent)
839
871
}
840
872
873
+ if (forceBreakBody) {
874
+ builder.forcedBreak()
875
+ }
876
+
841
877
if (hasStatements) {
842
878
builder.breakOp(Doc .FillMode .UNIFIED , " " , bracePlusBlockIndent)
843
879
builder.block(bracePlusBlockIndent) {
@@ -985,6 +1021,20 @@ class KotlinInputAstVisitor(
985
1021
986
1022
/* * Example `a` in `foo(a)`, or `*a`, or `limit = 50` */
987
1023
override fun visitArgument (argument : KtValueArgument ) {
1024
+ visitArgumentInternal(argument, forceBreakLambdaBody = false )
1025
+ }
1026
+
1027
+ /* *
1028
+ * The internal version of [visitArgument].
1029
+ *
1030
+ * @param forceBreakLambdaBody if true (and [argument] is of type [KtLambdaExpression]), forces
1031
+ * the lambda to be multi-line. See documentation of [visitLambdaExpressionInternal] for an
1032
+ * example.
1033
+ */
1034
+ private fun visitArgumentInternal (
1035
+ argument : KtValueArgument ,
1036
+ forceBreakLambdaBody : Boolean ,
1037
+ ) {
988
1038
builder.sync(argument)
989
1039
val hasArgName = argument.getArgumentName() != null
990
1040
val isLambda = argument.getArgumentExpression() is KtLambdaExpression
@@ -1004,7 +1054,15 @@ class KotlinInputAstVisitor(
1004
1054
if (argument.isSpread) {
1005
1055
builder.token(" *" )
1006
1056
}
1007
- visit(argument.getArgumentExpression())
1057
+ if (isLambda) {
1058
+ visitLambdaExpressionInternal(
1059
+ argument.getArgumentExpression() as KtLambdaExpression ,
1060
+ brokeBeforeBrace = null ,
1061
+ forceBreakBody = forceBreakLambdaBody,
1062
+ )
1063
+ } else {
1064
+ visit(argument.getArgumentExpression())
1065
+ }
1008
1066
}
1009
1067
}
1010
1068
}
@@ -1274,17 +1332,22 @@ class KotlinInputAstVisitor(
1274
1332
val breakToExpr = genSym()
1275
1333
builder.breakOp(Doc .FillMode .INDEPENDENT , " " , expressionBreakIndent, Optional .of(breakToExpr))
1276
1334
1277
- when (expr) {
1278
- is KtLambdaExpression -> {
1279
- visitLambdaExpression(expr, breakToExpr)
1280
- }
1281
- is KtCallExpression -> {
1282
- visit(expr.calleeExpression)
1283
- builder.space()
1284
- visitLambdaExpression(expr.lambdaArguments[0 ].getLambdaExpression() ? : fail(), breakToExpr)
1285
- }
1286
- else -> throw AssertionError (expr)
1287
- }
1335
+ val lambdaExpression =
1336
+ when (expr) {
1337
+ is KtLambdaExpression -> expr
1338
+ is KtCallExpression -> {
1339
+ visit(expr.calleeExpression)
1340
+ builder.space()
1341
+ expr.lambdaArguments[0 ].getLambdaExpression() ? : fail()
1342
+ }
1343
+ else -> throw AssertionError (expr)
1344
+ }
1345
+
1346
+ visitLambdaExpressionInternal(
1347
+ lambdaExpression,
1348
+ brokeBeforeBrace = breakToExpr,
1349
+ forceBreakBody = false ,
1350
+ )
1288
1351
}
1289
1352
1290
1353
override fun visitClassOrObject (classOrObject : KtClassOrObject ) {
0 commit comments