Initial support for chained call wrapping options

#KT-21529 Fixed
This commit is contained in:
Dmitry Jemerov
2017-11-30 16:17:09 +01:00
parent 1a93d10697
commit dc4e673fb1
7 changed files with 124 additions and 5 deletions
@@ -34,9 +34,11 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.children
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.siblings
private val QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS)
private val QUALIFIED_EXPRESSIONS = TokenSet.create(KtNodeTypes.DOT_QUALIFIED_EXPRESSION, KtNodeTypes.SAFE_ACCESS_EXPRESSION)
private val KDOC_COMMENT_INDENT = 1
private val BINARY_EXPRESSIONS = TokenSet.create(KtNodeTypes.BINARY_EXPRESSION, KtNodeTypes.BINARY_WITH_TYPE, KtNodeTypes.IS_EXPRESSION)
@@ -92,7 +94,7 @@ abstract class KotlinCommonBlock(
var nodeSubBlocks = buildSubBlocks()
if (node.elementType === KtNodeTypes.DOT_QUALIFIED_EXPRESSION || node.elementType === KtNodeTypes.SAFE_ACCESS_EXPRESSION) {
if (node.elementType in QUALIFIED_EXPRESSIONS) {
val operationBlockIndex = findNodeBlockIndex(nodeSubBlocks, QUALIFIED_OPERATION)
if (operationBlockIndex != -1) {
// Create fake ".something" or "?.something" block here, so child indentation will be
@@ -103,10 +105,17 @@ abstract class KotlinCommonBlock(
Indent.getContinuationWithoutFirstIndent()
else
Indent.getNormalIndent()
val isNonFirstChainedCall = operationBlockIndex > 0 && isCallBlock(nodeSubBlocks[operationBlockIndex - 1])
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
val operationSyntheticBlock = SyntheticKotlinBlock(
operationBlock.node,
nodeSubBlocks.subList(operationBlockIndex, nodeSubBlocks.size),
null, indent, null, spacingBuilder) { createSyntheticSpacingNodeBlock(it) }
null, indent, wrap, spacingBuilder) { createSyntheticSpacingNodeBlock(it) }
nodeSubBlocks = nodeSubBlocks.subList(0, operationBlockIndex) + operationSyntheticBlock
}
@@ -117,6 +126,20 @@ abstract class KotlinCommonBlock(
return nodeSubBlocks
}
private fun isCallBlock(astBlock: ASTBlock): Boolean {
val node = astBlock.node
return node.elementType in QUALIFIED_EXPRESSIONS && node.lastChildNode?.elementType == KtNodeTypes.CALL_EXPRESSION
}
private fun canWrapCallChain(node: ASTNode): Boolean {
val callChainParent = node.parents().firstOrNull { it.elementType !in QUALIFIED_EXPRESSIONS } ?: return true
return callChainParent.elementType in CODE_BLOCKS ||
callChainParent.elementType == KtNodeTypes.PROPERTY ||
(callChainParent.elementType == KtNodeTypes.BINARY_EXPRESSION &&
(callChainParent.psi as KtBinaryExpression).operationToken in KtTokens.ALL_ASSIGNMENTS) ||
callChainParent.elementType == KtNodeTypes.RETURN
}
fun createChildIndent(child: ASTNode): Indent? {
val childParent = child.treeParent
val childType = child.elementType
@@ -182,7 +205,7 @@ abstract class KotlinCommonBlock(
KtNodeTypes.TRY -> ChildAttributes(Indent.getNoneIndent(), null)
KtNodeTypes.DOT_QUALIFIED_EXPRESSION, KtNodeTypes.SAFE_ACCESS_EXPRESSION -> ChildAttributes(Indent.getContinuationWithoutFirstIndent(), null)
in QUALIFIED_EXPRESSIONS -> ChildAttributes(Indent.getContinuationWithoutFirstIndent(), null)
KtNodeTypes.VALUE_PARAMETER_LIST, KtNodeTypes.VALUE_ARGUMENT_LIST -> {
val subBlocks = getSubBlocks()
@@ -463,7 +486,7 @@ private val INDENT_RULES = arrayOf<NodeIndentStrategy>(
.set(Indent.getContinuationWithoutFirstIndent()),
strategy("Chained calls")
.within(KtNodeTypes.DOT_QUALIFIED_EXPRESSION, KtNodeTypes.SAFE_ACCESS_EXPRESSION)
.within(QUALIFIED_EXPRESSIONS)
.notForType(KtTokens.DOT, KtTokens.SAFE_ACCESS)
.forElement { it.treeParent.firstChildNode != it }
.set { settings ->