Formatter: support Chop down if long for Chained method calls

#KT-23929 Fixed
#KT-33553 Fixed
This commit is contained in:
Dmitry Gridin
2019-11-11 14:33:04 +07:00
parent 351fef5628
commit b6b755506c
13 changed files with 726 additions and 54 deletions
@@ -25,10 +25,12 @@ import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes
import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS) private val QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS)
private val QUALIFIED_EXPRESSIONS = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION) private val QUALIFIED_EXPRESSIONS = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION)
private val ELVIS_SET = TokenSet.create(ELVIS) private val ELVIS_SET = TokenSet.create(ELVIS)
private val QUALIFIED_EXPRESSIONS_WITHOUT_WRAP = TokenSet.create(IMPORT_DIRECTIVE, PACKAGE_DIRECTIVE)
private const val KDOC_COMMENT_INDENT = 1 private const val KDOC_COMMENT_INDENT = 1
@@ -120,46 +122,107 @@ abstract class KotlinCommonBlock(
return nodeSubBlocks return nodeSubBlocks
} }
private fun ASTNode.isQualifiedNode(): Boolean {
var currentNode: ASTNode? = this
while (currentNode != null) {
if (currentNode.elementType in QUALIFIED_EXPRESSIONS) return true
if (currentNode.elementType != PARENTHESIZED && currentNode.psi?.safeAs<KtPostfixExpression>()?.operationToken != EXCLEXCL) return false
currentNode = currentNode.treeParent
}
return false
}
private fun splitSubBlocksOnDot(nodeSubBlocks: List<ASTBlock>): List<ASTBlock> { private fun splitSubBlocksOnDot(nodeSubBlocks: List<ASTBlock>): List<ASTBlock> {
if (node.treeParent?.isQualifiedNode() == true) return nodeSubBlocks
if (!canWrapCallChain(node)) return nodeSubBlocks
val operationBlockIndex = nodeSubBlocks.indexOfBlockWithType(QUALIFIED_OPERATION) val operationBlockIndex = nodeSubBlocks.indexOfBlockWithType(QUALIFIED_OPERATION)
if (operationBlockIndex != -1) { if (operationBlockIndex == -1) return nodeSubBlocks
// Create fake ".something" or "?.something" block here, so child indentation will be
// relative to it when it starts from new line (see Indent javadoc).
val isNonFirstChainedCall = operationBlockIndex > 0 && isCallBlock(nodeSubBlocks[operationBlockIndex - 1]) val block = nodeSubBlocks.first()
val wrap = createWrapForQualifiedExpression(block.requireNode())
val indent = createIndentForQualifiedExpression(block)
val newBlock = block.processBlock(indent, wrap)
return nodeSubBlocks.replaceBlock(newBlock, 0).splitAtIndex(operationBlockIndex, indent, wrap)
}
private fun ASTBlock.processBlock(indent: Indent, wrap: Wrap?): ASTBlock {
val currentNode = requireNode()
@Suppress("UNCHECKED_CAST")
val subBlocks = subBlocks as List<ASTBlock>
val elementType = currentNode.elementType
val index = when (elementType) {
PARENTHESIZED -> subBlocks.indexOfFirst { block ->
val type = block.requireNode().elementType
type != LPAR && type !in WHITE_SPACE_OR_COMMENT_BIT_SET
}
POSTFIX_EXPRESSION, in QUALIFIED_EXPRESSIONS -> 0
else -> return this
}
val resultWrap = wrap.takeIf { elementType != PARENTHESIZED } ?: createWrapForQualifiedExpression(currentNode)
val newBlock = subBlocks.elementAt(index).processBlock(indent, resultWrap)
return subBlocks.replaceBlock(newBlock, index).let {
val operationIndex = subBlocks.indexOfBlockWithType(QUALIFIED_OPERATION)
if (operationIndex != -1)
it.splitAtIndex(operationIndex, indent, resultWrap)
else
it
}.wrapToBlock(currentNode, this)
}
private fun List<ASTBlock>.replaceBlock(
block: ASTBlock,
index: Int = 0
): List<ASTBlock> = subList(0, index) + block + (takeIf { index + 1 < it.size }?.subList(index + 1, size) ?: emptyList())
private fun createWrapForQualifiedExpression(node: ASTNode): Wrap? {
val isNonFirstChainedCall = (node.treeParent)?.isQualifiedNode() == true
return if (isNonFirstChainedCall)
Wrap.createWrap(
settings.kotlinCommonSettings.METHOD_CALL_CHAIN_WRAP,
settings.kotlinCommonSettings.WRAP_FIRST_METHOD_IN_CALL_CHAIN
)
else
null
}
private fun createIndentForQualifiedExpression(block: ASTBlock): 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)
val enforceIndentToChildren = anyCallInCallChainIsWrapped(nodeSubBlocks[operationBlockIndex - 1]) val enforceIndentToChildren = anyCallInCallChainIsWrapped(block)
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 {
Indent.Type.NORMAL Indent.Type.NORMAL
} }
val indent = Indent.getIndent(
return Indent.getIndent(
indentType, false, indentType, false,
enforceIndentToChildren enforceIndentToChildren
) )
val wrap = if ((settings.kotlinCommonSettings.WRAP_FIRST_METHOD_IN_CALL_CHAIN || isNonFirstChainedCall) &&
canWrapCallChain(node)
)
Wrap.createWrap(settings.kotlinCommonSettings.METHOD_CALL_CHAIN_WRAP, true)
else
null
return nodeSubBlocks.splitAtIndex(operationBlockIndex, indent, wrap)
}
return nodeSubBlocks
} }
private fun List<ASTBlock>.splitAtIndex(index: Int, indent: Indent?, wrap: Wrap?): List<ASTBlock> { private fun List<ASTBlock>.wrapToBlock(
anchor: ASTNode?,
parentBlock: ASTBlock?
): ASTBlock = splitAtIndex(0, null, null, anchor, parentBlock).single()
private fun List<ASTBlock>.splitAtIndex(
index: Int,
indent: Indent?,
wrap: Wrap?,
anchor: ASTNode? = null,
parentBlock: ASTBlock? = null
): List<ASTBlock> {
val operationBlock = this[index] val operationBlock = this[index]
val operationSyntheticBlock = SyntheticKotlinBlock( val createParentSyntheticSpacingBlock: (ASTNode) -> ASTBlock = if (parentBlock != null) {
operationBlock.requireNode(), { parentBlock }
subList(index, size), } else {
null, indent, wrap, spacingBuilder {
) {
val parent = it.treeParent ?: node val parent = it.treeParent ?: node
val skipOperationNodeParent = if (parent.elementType === OPERATION_REFERENCE) { val skipOperationNodeParent = if (parent.elementType === OPERATION_REFERENCE) {
parent.treeParent ?: parent parent.treeParent ?: parent
@@ -168,6 +231,12 @@ abstract class KotlinCommonBlock(
} }
createSyntheticSpacingNodeBlock(skipOperationNodeParent) createSyntheticSpacingNodeBlock(skipOperationNodeParent)
} }
}
val operationSyntheticBlock = SyntheticKotlinBlock(
anchor ?: operationBlock.requireNode(),
subList(index, size),
null, indent, wrap, spacingBuilder, createParentSyntheticSpacingBlock
)
return subList(0, index) + operationSyntheticBlock return subList(0, index) + operationSyntheticBlock
} }
@@ -186,16 +255,13 @@ abstract class KotlinCommonBlock(
private fun isCallBlock(astBlock: ASTBlock): Boolean { private fun isCallBlock(astBlock: ASTBlock): Boolean {
val node = astBlock.requireNode() val node = astBlock.requireNode()
return node.elementType in QUALIFIED_EXPRESSIONS && node.lastChildNode?.elementType == CALL_EXPRESSION val lastChildElementType = node.lastChildNode?.elementType
return node.isQualifiedNode() && (lastChildElementType == CALL_EXPRESSION || lastChildElementType == REFERENCE_EXPRESSION)
} }
private fun canWrapCallChain(node: ASTNode): Boolean { private fun canWrapCallChain(node: ASTNode): Boolean {
val callChainParent = node.parents().firstOrNull { it.elementType !in QUALIFIED_EXPRESSIONS } ?: return true val callChainParent = node.parents().firstOrNull { !it.isQualifiedNode() } ?: return true
return callChainParent.elementType in CODE_BLOCKS || return callChainParent.elementType !in QUALIFIED_EXPRESSIONS_WITHOUT_WRAP
callChainParent.elementType == PROPERTY ||
(callChainParent.elementType == BINARY_EXPRESSION &&
(callChainParent.psi as KtBinaryExpression).operationToken in ALL_ASSIGNMENTS) ||
callChainParent.elementType == RETURN
} }
private fun splitSubBlocksOnElvis(nodeSubBlocks: List<ASTBlock>): List<ASTBlock> { private fun splitSubBlocksOnElvis(nodeSubBlocks: List<ASTBlock>): List<ASTBlock> {
@@ -4,7 +4,9 @@ interface Test {
fun test(t: Test) { fun test(t: Test) {
t.foo() t.foo()
t().foo()
t()!!.foo().foo()
((t()!!).foo().foo().foo()).foo()
t.foo() t.foo()
t.foo() t.foo()
+3 -1
View File
@@ -4,7 +4,9 @@ interface Test {
fun test(t: Test) { fun test(t: Test) {
t . foo() t . foo()
t() . foo()
t() !! . foo() .foo()
( ( t() !!) . foo() .foo() .foo()) .foo()
t. t.
foo() foo()
@@ -3,7 +3,10 @@ val x = foo
.baz() .baz()
.quux() .quux()
val y = xyzzy(foo.bar().baz().quux()) val y = xyzzy(foo
.bar()
.baz()
.quux())
fun foo() { fun foo() {
foo foo
@@ -27,5 +30,25 @@ fun foo() {
.quux() .quux()
} }
fun top() = ""
.plus("")
.plus("")
class C {
fun member() = ""
.plus("")
.plus("")
}
fun foo() {
fun local() = ""
.plus("")
.plus("")
val anonymous = fun() = ""
.plus("")
.plus("")
}
// SET_INT: METHOD_CALL_CHAIN_WRAP = 2 // SET_INT: METHOD_CALL_CHAIN_WRAP = 2
// SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN // SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
@@ -2,7 +2,9 @@ val x = foo.bar()
.baz() .baz()
.quux() .quux()
val y = xyzzy(foo.bar().baz().quux()) val y = xyzzy(foo.bar()
.baz()
.quux())
fun foo() { fun foo() {
foo.bar() foo.bar()
@@ -22,5 +24,21 @@ fun foo() {
.quux() .quux()
} }
fun top() = "".plus("")
.plus("")
class C {
fun member() = "".plus("")
.plus("")
}
fun foo() {
fun local() = "".plus("")
.plus("")
val anonymous = fun() = "".plus("")
.plus("")
}
// SET_INT: METHOD_CALL_CHAIN_WRAP = 2 // SET_INT: METHOD_CALL_CHAIN_WRAP = 2
// SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN // SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
@@ -12,5 +12,14 @@ fun foo() {
return foo.bar().baz().quux() return foo.bar().baz().quux()
} }
fun top() = "".plus("").plus("")
class C {
fun member() = "".plus("").plus("")
}
fun foo() {
fun local() = "".plus("").plus("")
val anonymous = fun() = "".plus("").plus("")
}
// SET_INT: METHOD_CALL_CHAIN_WRAP = 2 // SET_INT: METHOD_CALL_CHAIN_WRAP = 2
// SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN // SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
@@ -0,0 +1,244 @@
val x = foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
val y = xyzzy(foo.bar().baz().quux())
fun foo() {
foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
z = (((foo()))!!).bar()
z += (foo.bar().baz().quux().foo.bar().baz().quux().foo.bar()!!)
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
return ((foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux())
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar())
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
}
// SET_INT: METHOD_CALL_CHAIN_WRAP = 5
// SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
// SET_FALSE: CONTINUATION_INDENT_FOR_CHAINED_CALLS
@@ -0,0 +1,239 @@
val x = foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
val y = xyzzy(foo.bar().baz().quux())
fun foo() {
foo.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
z = (((foo()))!!).bar()
z += (foo.bar().baz().quux().foo.bar().baz().quux().foo.bar()!!).baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
return ((foo.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()).foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()).baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
.foo
.bar()
.baz()
.quux()
}
// SET_INT: METHOD_CALL_CHAIN_WRAP = 5
// SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
// SET_FALSE: CONTINUATION_INDENT_FOR_CHAINED_CALLS
@@ -0,0 +1,19 @@
val x = foo
.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux()
val y = xyzzy(foo.bar().baz().quux())
fun foo() {
foo.bar().baz().quux()
.foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux()
z = (( ( foo()) )!! ).bar()
z += (foo.bar().baz().quux().foo.bar().baz().quux().foo.bar() !!) .baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux()
return ((foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux()).foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar()).baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux().foo.bar().baz().quux()
}
// SET_INT: METHOD_CALL_CHAIN_WRAP = 5
// SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
// SET_FALSE: CONTINUATION_INDENT_FOR_CHAINED_CALLS
@@ -0,0 +1,12 @@
fun f(list: List<Int>) {
val result = list
.filter { it % 2 == 0 }
.max() ?: -1
list
.filter { it % 2 == 0 }
.max()
?: -1
}
// SET_INT: METHOD_CALL_CHAIN_WRAP = 2
// SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
@@ -0,0 +1,10 @@
fun f(list: List<Int>) {
val result = list.filter { it % 2 == 0 }
.max() ?: -1
list.filter { it % 2 == 0 }
.max()
?: -1
}
// SET_INT: METHOD_CALL_CHAIN_WRAP = 2
// SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
@@ -0,0 +1,8 @@
fun f(list: List<Int>) {
val result = list.filter { it % 2 == 0 }.max() ?: -1
list.filter { it % 2 == 0 }.max()
?: -1
}
// SET_INT: METHOD_CALL_CHAIN_WRAP = 2
// SET_FALSE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
@@ -852,6 +852,16 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
runTest("idea/testData/formatter/callChain/CallChainWrapping.after.kt"); runTest("idea/testData/formatter/callChain/CallChainWrapping.after.kt");
} }
@TestMetadata("CallChainWrappingChopDown.after.kt")
public void testCallChainWrappingChopDown() throws Exception {
runTest("idea/testData/formatter/callChain/CallChainWrappingChopDown.after.kt");
}
@TestMetadata("CallChainWrappingInElvis.after.kt")
public void testCallChainWrappingInElvis() throws Exception {
runTest("idea/testData/formatter/callChain/CallChainWrappingInElvis.after.kt");
}
@TestMetadata("ConsecutiveCalls.after.kt") @TestMetadata("ConsecutiveCalls.after.kt")
public void testConsecutiveCalls() throws Exception { public void testConsecutiveCalls() throws Exception {
runTest("idea/testData/formatter/callChain/ConsecutiveCalls.after.kt"); runTest("idea/testData/formatter/callChain/ConsecutiveCalls.after.kt");
@@ -1397,6 +1407,16 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
runTest("idea/testData/formatter/callChain/CallChainWrapping.after.inv.kt"); runTest("idea/testData/formatter/callChain/CallChainWrapping.after.inv.kt");
} }
@TestMetadata("CallChainWrappingChopDown.after.inv.kt")
public void testCallChainWrappingChopDown() throws Exception {
runTest("idea/testData/formatter/callChain/CallChainWrappingChopDown.after.inv.kt");
}
@TestMetadata("CallChainWrappingInElvis.after.inv.kt")
public void testCallChainWrappingInElvis() throws Exception {
runTest("idea/testData/formatter/callChain/CallChainWrappingInElvis.after.inv.kt");
}
@TestMetadata("ConsecutiveSafeCallsIndent.after.inv.kt") @TestMetadata("ConsecutiveSafeCallsIndent.after.inv.kt")
public void testConsecutiveSafeCallsIndent() throws Exception { public void testConsecutiveSafeCallsIndent() throws Exception {
runTest("idea/testData/formatter/callChain/ConsecutiveSafeCallsIndent.after.inv.kt"); runTest("idea/testData/formatter/callChain/ConsecutiveSafeCallsIndent.after.inv.kt");