aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/java/com/facebook
diff options
context:
space:
mode:
authorDavid Torosyan <dtoro@fb.com>2022-05-26 11:34:20 -0700
committerFacebook GitHub Bot <facebook-github-bot@users.noreply.github.com>2022-05-26 11:34:20 -0700
commitc0e6243c33a1b9b4afbf9160ee3670a3387f0314 (patch)
tree1c50d323c5137d550a04d88b587790074eef65df /core/src/main/java/com/facebook
parentbd2e5c73c94673991aa5a34090337c92e650197d (diff)
downloadktfmt-c0e6243c33a1b9b4afbf9160ee3670a3387f0314.tar.gz
Add line breaks to lambdas after broken function arguments
Summary: We've had some requests to format this: ``` foo.bar( trailingComma, ) { x = 0 } ``` As this: ``` foo.bar( trailingComma, ) { x = 0 } ``` Here I do that by keeping track of when we enter lambda arguments in call elements and noting if there's a trailing comma. ## Caveat **Google-style requires a trailing comma** The Google-style formatting puts the closing paren on the next line, even when there's no trailing comma. Due to this we can't detect that we need to put a break in the lambda. This could be fixed by detecting if the params broke (instead of looking for a trailing comma) but I'm not sure how to do that. Reviewed By: strulovich Differential Revision: D36649191 fbshipit-source-id: 2c8d316f068c1030437af59b5114ffae9d5d2aa8
Diffstat (limited to 'core/src/main/java/com/facebook')
-rw-r--r--core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt93
1 files changed, 78 insertions, 15 deletions
diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt
index 1a0136a..cfa58c3 100644
--- a/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt
+++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt
@@ -522,6 +522,7 @@ class KotlinInputAstVisitor(
}
val argsIndentElse = if (index == parts.size - 1) ZERO else expressionBreakIndent
val lambdaIndentElse = if (isTrailingLambda) expressionBreakNegativeIndent else ZERO
+
// emit `(1, 2) { it }` from `doIt(1, 2) { it }`
visitCallElement(
null,
@@ -751,9 +752,14 @@ class KotlinInputAstVisitor(
builder.token(")")
}
}
+ val hasTrailingComma = argumentList?.trailingComma != null
if (lambdaArguments.isNotEmpty()) {
builder.space()
- builder.block(lambdaIndent) { lambdaArguments.forEach { visit(it) } }
+ builder.block(lambdaIndent) {
+ lambdaArguments.forEach {
+ visitArgumentInternal(it, forceBreakLambdaBody = hasTrailingComma)
+ }
+ }
}
}
}
@@ -783,12 +789,38 @@ class KotlinInputAstVisitor(
/** Example `{ 1 + 1 }` (as lambda) or `{ (x, y) -> x + y }` */
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
- visitLambdaExpression(lambdaExpression, null as BreakTag?)
+ visitLambdaExpressionInternal(lambdaExpression, brokeBeforeBrace = null, forceBreakBody = false)
}
- private fun visitLambdaExpression(
+ /**
+ * The internal version of [visitLambdaExpression].
+ *
+ * @param brokeBeforeBrace used for tracking if a break was taken right before the lambda
+ * expression. Useful for scoping functions where we want good looking indentation. For example,
+ * here we have correct indentation before `bar()` and `car()` because we can detect the break
+ * after the equals:
+ * ```
+ * fun foo() =
+ * coroutineScope { x ->
+ * bar()
+ * car()
+ * }
+ * ```
+ * @param forceBreakBody if true, forces the lambda to be multi-line. Useful for call expressions
+ * where it would look weird for the lambda to be on one-line. For example, here we avoid
+ * one-lining `{ x = 0 }` since the parameters have a trailing comma:
+ * ```
+ * foo.bar(
+ * trailingComma,
+ * ) {
+ * x = 0
+ * }
+ * ```
+ */
+ private fun visitLambdaExpressionInternal(
lambdaExpression: KtLambdaExpression,
brokeBeforeBrace: BreakTag?,
+ forceBreakBody: Boolean,
) {
builder.sync(lambdaExpression)
@@ -838,6 +870,10 @@ class KotlinInputAstVisitor(
builder.breakOp(Doc.FillMode.UNIFIED, "", bracePlusZeroIndent)
}
+ if (forceBreakBody) {
+ builder.forcedBreak()
+ }
+
if (hasStatements) {
builder.breakOp(Doc.FillMode.UNIFIED, " ", bracePlusBlockIndent)
builder.block(bracePlusBlockIndent) {
@@ -985,6 +1021,20 @@ class KotlinInputAstVisitor(
/** Example `a` in `foo(a)`, or `*a`, or `limit = 50` */
override fun visitArgument(argument: KtValueArgument) {
+ visitArgumentInternal(argument, forceBreakLambdaBody = false)
+ }
+
+ /**
+ * The internal version of [visitArgument].
+ *
+ * @param forceBreakLambdaBody if true (and [argument] is of type [KtLambdaExpression]), forces
+ * the lambda to be multi-line. See documentation of [visitLambdaExpressionInternal] for an
+ * example.
+ */
+ private fun visitArgumentInternal(
+ argument: KtValueArgument,
+ forceBreakLambdaBody: Boolean,
+ ) {
builder.sync(argument)
val hasArgName = argument.getArgumentName() != null
val isLambda = argument.getArgumentExpression() is KtLambdaExpression
@@ -1004,7 +1054,15 @@ class KotlinInputAstVisitor(
if (argument.isSpread) {
builder.token("*")
}
- visit(argument.getArgumentExpression())
+ if (isLambda) {
+ visitLambdaExpressionInternal(
+ argument.getArgumentExpression() as KtLambdaExpression,
+ brokeBeforeBrace = null,
+ forceBreakBody = forceBreakLambdaBody,
+ )
+ } else {
+ visit(argument.getArgumentExpression())
+ }
}
}
}
@@ -1274,17 +1332,22 @@ class KotlinInputAstVisitor(
val breakToExpr = genSym()
builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent, Optional.of(breakToExpr))
- when (expr) {
- is KtLambdaExpression -> {
- visitLambdaExpression(expr, breakToExpr)
- }
- is KtCallExpression -> {
- visit(expr.calleeExpression)
- builder.space()
- visitLambdaExpression(expr.lambdaArguments[0].getLambdaExpression() ?: fail(), breakToExpr)
- }
- else -> throw AssertionError(expr)
- }
+ val lambdaExpression =
+ when (expr) {
+ is KtLambdaExpression -> expr
+ is KtCallExpression -> {
+ visit(expr.calleeExpression)
+ builder.space()
+ expr.lambdaArguments[0].getLambdaExpression() ?: fail()
+ }
+ else -> throw AssertionError(expr)
+ }
+
+ visitLambdaExpressionInternal(
+ lambdaExpression,
+ brokeBeforeBrace = breakToExpr,
+ forceBreakBody = false,
+ )
}
override fun visitClassOrObject(classOrObject: KtClassOrObject) {