Formatter: fix continuation indent in call chain
#KT-37545 Fixed
This commit is contained in:
@@ -131,13 +131,16 @@ abstract class KotlinCommonBlock(
|
|||||||
|
|
||||||
val block = nodeSubBlocks.first()
|
val block = nodeSubBlocks.first()
|
||||||
val wrap = createWrapForQualifierExpression(node)
|
val wrap = createWrapForQualifierExpression(node)
|
||||||
val indent = createIndentForQualifierExpression(node)
|
val enforceIndentToChildren = anyCallInCallChainIsWrapped(node)
|
||||||
val newBlock = block.processBlock(indent, wrap)
|
val indent = createIndentForQualifierExpression(enforceIndentToChildren)
|
||||||
|
val newBlock = block.processBlock(wrap, enforceIndentToChildren)
|
||||||
return nodeSubBlocks.replaceBlock(newBlock, 0).splitAtIndex(operationBlockIndex, indent, wrap)
|
return nodeSubBlocks.replaceBlock(newBlock, 0).splitAtIndex(operationBlockIndex, indent, wrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ASTBlock.processBlock(indent: Indent, wrap: Wrap?): ASTBlock {
|
private fun ASTBlock.processBlock(wrap: Wrap?, enforceIndentToChildren: Boolean): ASTBlock {
|
||||||
val currentNode = requireNode()
|
val currentNode = requireNode()
|
||||||
|
val enforceIndent = enforceIndentToChildren && anyCallInCallChainIsWrapped(currentNode)
|
||||||
|
val indent = createIndentForQualifierExpression(enforceIndent)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val subBlocks = subBlocks as List<ASTBlock>
|
val subBlocks = subBlocks as List<ASTBlock>
|
||||||
@@ -150,8 +153,7 @@ abstract class KotlinCommonBlock(
|
|||||||
else
|
else
|
||||||
null
|
null
|
||||||
|
|
||||||
val newBlock = subBlocks.elementAt(index).processBlock(indent, resultWrap)
|
val newBlock = subBlocks.elementAt(index).processBlock(resultWrap, enforceIndent)
|
||||||
|
|
||||||
return subBlocks.replaceBlock(newBlock, index).let {
|
return subBlocks.replaceBlock(newBlock, index).let {
|
||||||
val operationIndex = subBlocks.indexOfBlockWithType(QUALIFIED_OPERATION)
|
val operationIndex = subBlocks.indexOfBlockWithType(QUALIFIED_OPERATION)
|
||||||
if (operationIndex != -1)
|
if (operationIndex != -1)
|
||||||
@@ -178,11 +180,9 @@ abstract class KotlinCommonBlock(
|
|||||||
else
|
else
|
||||||
null
|
null
|
||||||
|
|
||||||
private fun createIndentForQualifierExpression(node: ASTNode): Indent {
|
// enforce indent to children when there's a line break before the dot in any call in the chain (meaning that
|
||||||
// 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)
|
||||||
// the call chain following that call is indented)
|
private fun createIndentForQualifierExpression(enforceIndentToChildren: Boolean): Indent {
|
||||||
val enforceIndentToChildren = anyCallInCallChainIsWrapped(node)
|
|
||||||
|
|
||||||
val indentType = if (settings.kotlinCustomSettings.CONTINUATION_INDENT_FOR_CHAINED_CALLS) {
|
val indentType = if (settings.kotlinCustomSettings.CONTINUATION_INDENT_FOR_CHAINED_CALLS) {
|
||||||
if (enforceIndentToChildren) Indent.Type.CONTINUATION else Indent.Type.CONTINUATION_WITHOUT_FIRST
|
if (enforceIndentToChildren) Indent.Type.CONTINUATION else Indent.Type.CONTINUATION_WITHOUT_FIRST
|
||||||
} else {
|
} else {
|
||||||
@@ -825,11 +825,22 @@ private val ASTNode.isCall: Boolean
|
|||||||
get() = unwrapQualifier()?.lastChildNode?.elementType == CALL_EXPRESSION
|
get() = unwrapQualifier()?.lastChildNode?.elementType == CALL_EXPRESSION
|
||||||
|
|
||||||
private fun anyCallInCallChainIsWrapped(node: ASTNode): Boolean {
|
private fun anyCallInCallChainIsWrapped(node: ASTNode): Boolean {
|
||||||
val sequentialCalls = generateSequence(node) { it.firstChildNode }.takeWhile { it.isCall }
|
val sequentialNodes = generateSequence(node) {
|
||||||
|
when (it.elementType) {
|
||||||
|
POSTFIX_EXPRESSION, in QUALIFIED_EXPRESSIONS -> it.firstChildNode
|
||||||
|
PARENTHESIZED -> getSiblingWithoutWhitespaceAndComments(it.firstChildNode, true)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sequentialCalls.any { call ->
|
return sequentialNodes.any {
|
||||||
val qualifier = call.findChildByType(QUALIFIED_OPERATION)
|
val checkedElement = when (it.elementType) {
|
||||||
qualifier != null && hasLineBreakBefore(qualifier)
|
in QUALIFIED_EXPRESSIONS -> it.findChildByType(QUALIFIED_OPERATION)
|
||||||
|
PARENTHESIZED -> it.lastChildNode
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
checkedElement != null && hasLineBreakBefore(checkedElement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
import X.combine
|
||||||
|
|
||||||
|
class IndentationIssue {
|
||||||
|
fun method() {
|
||||||
|
X.Y.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X.Y.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X.Y./*
|
||||||
|
comment
|
||||||
|
*/combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X.Y./*
|
||||||
|
comment
|
||||||
|
*/combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
(X.Y.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X.Y
|
||||||
|
.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let { "zzz" }
|
||||||
|
|
||||||
|
X.Y
|
||||||
|
.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X
|
||||||
|
.Y
|
||||||
|
.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let { "zzz" }
|
||||||
|
|
||||||
|
X
|
||||||
|
.Y.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let { "zzz" }
|
||||||
|
|
||||||
|
combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let { "zzz" }
|
||||||
|
|
||||||
|
combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
(combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
(combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
}).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
}!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
(combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
})!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
((combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}!!)!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
((combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)).let {
|
||||||
|
"zzz"
|
||||||
|
}!!)!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
((combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)).let {
|
||||||
|
"zzz"
|
||||||
|
}!!
|
||||||
|
)!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object X {
|
||||||
|
fun <L, R, T> combine(x: L, y: R): T = TODO()
|
||||||
|
|
||||||
|
val Y get() = this
|
||||||
|
}
|
||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
import X.combine
|
||||||
|
|
||||||
|
class IndentationIssue {
|
||||||
|
fun method() {
|
||||||
|
X.Y.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X.Y.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X.Y./*
|
||||||
|
comment
|
||||||
|
*/combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X.Y./*
|
||||||
|
comment
|
||||||
|
*/combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
(X.Y.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X.Y
|
||||||
|
.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let { "zzz" }
|
||||||
|
|
||||||
|
X.Y
|
||||||
|
.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
X
|
||||||
|
.Y
|
||||||
|
.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let { "zzz" }
|
||||||
|
|
||||||
|
X
|
||||||
|
.Y.combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let { "zzz" }
|
||||||
|
|
||||||
|
combine(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.let { "zzz" }
|
||||||
|
|
||||||
|
combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
(combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
(combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
}).let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
}!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
(combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
.let {
|
||||||
|
"zzz"
|
||||||
|
})!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
((combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
).let {
|
||||||
|
"zzz"
|
||||||
|
}!!)!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
((combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)).let {
|
||||||
|
"zzz"
|
||||||
|
}!!)!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
|
||||||
|
((combine(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
)).let {
|
||||||
|
"zzz"
|
||||||
|
}!!
|
||||||
|
)!!.let {
|
||||||
|
"zzz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object X {
|
||||||
|
fun <L, R, T> combine(x: L, y: R): T = TODO()
|
||||||
|
|
||||||
|
val Y get() = this
|
||||||
|
}
|
||||||
@@ -937,6 +937,11 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
|
|||||||
runTest("idea/testData/formatter/callChain/KT36466.after.kt");
|
runTest("idea/testData/formatter/callChain/KT36466.after.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("KT37545.after.kt")
|
||||||
|
public void testKT37545() throws Exception {
|
||||||
|
runTest("idea/testData/formatter/callChain/KT37545.after.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("NotCallChain.after.kt")
|
@TestMetadata("NotCallChain.after.kt")
|
||||||
public void testNotCallChain() throws Exception {
|
public void testNotCallChain() throws Exception {
|
||||||
runTest("idea/testData/formatter/callChain/NotCallChain.after.kt");
|
runTest("idea/testData/formatter/callChain/NotCallChain.after.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user