Call chain indentation logic that actually makes sense

#KT-22346 Fixed
This commit is contained in:
Dmitry Jemerov
2018-02-04 11:00:00 +01:00
parent 2c88b26034
commit 36f5525b12
4 changed files with 40 additions and 4 deletions
@@ -127,7 +127,11 @@ abstract class KotlinCommonBlock(
// relative to it when it starts from new line (see Indent javadoc).
val isNonFirstChainedCall = operationBlockIndex > 0 && isCallBlock(nodeSubBlocks[operationBlockIndex - 1])
val enforceIndentToChildren = isNonFirstChainedCall && hasLineBreakBefore(nodeSubBlocks[operationBlockIndex])
// enforce indent to children when there's a line break before the dot in any call in the chain (meaning that
// the call chain following that call is indented)
val enforceIndentToChildren = anyCallInCallChainIsWrapped(nodeSubBlocks[operationBlockIndex - 1])
val indentType = if (settings.kotlinCustomSettings.CONTINUATION_INDENT_FOR_CHAINED_CALLS) {
if (enforceIndentToChildren) Indent.Type.CONTINUATION else Indent.Type.CONTINUATION_WITHOUT_FIRST
} else {
@@ -159,6 +163,18 @@ abstract class KotlinCommonBlock(
return subList(0, index) + operationSyntheticBlock
}
private fun anyCallInCallChainIsWrapped(astBlock: ASTBlock): Boolean {
var result: ASTBlock? = astBlock
while (true) {
if (result == null || !isCallBlock(result)) return false
val dot = result.node?.findChildByType(QUALIFIED_OPERATION)
if (dot != null && hasLineBreakBefore(dot)) {
return true
}
result = result.subBlocks.firstOrNull() as? ASTBlock?
}
}
private fun isCallBlock(astBlock: ASTBlock): Boolean {
val node = astBlock.node
return node.elementType in QUALIFIED_EXPRESSIONS && node.lastChildNode?.elementType == KtNodeTypes.CALL_EXPRESSION
@@ -602,9 +618,9 @@ fun needWrapArgumentList(psi: PsiElement): Boolean {
return args?.singleOrNull()?.getArgumentExpression() !is KtObjectLiteralExpression
}
private fun hasLineBreakBefore(block: ASTBlock): Boolean {
val prevSibling = block.node.leaves(false)
.dropWhile { it.psi is PsiComment || it.elementType == KtTokens.RBRACE }
private fun hasLineBreakBefore(node: ASTNode): Boolean {
val prevSibling = node.leaves(false)
.dropWhile { it.psi is PsiComment }
.firstOrNull()
return prevSibling?.elementType == TokenType.WHITE_SPACE && prevSibling?.textContains('\n') == true
}
+7
View File
@@ -0,0 +1,7 @@
val x = foo.bar {
it + 2
}.let {
println(it)
}
// SET_FALSE: CONTINUATION_INDENT_FOR_CHAINED_CALLS
+7
View File
@@ -0,0 +1,7 @@
val x = foo.bar {
it + 2
}.let {
println(it)
}
// SET_FALSE: CONTINUATION_INDENT_FOR_CHAINED_CALLS
@@ -988,6 +988,12 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/callChain/KT22148.after.kt");
doTest(fileName);
}
@TestMetadata("KT22346.after.kt")
public void testKT22346() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/callChain/KT22346.after.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/formatter/fileAnnotations")