From 79a509df7b3498b9ad92366bc5104b28cc016f79 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 13 Dec 2017 16:29:27 +0100 Subject: [PATCH] Option to apply normal indent to children of 'if' expressions --- .../formatter/KotlinCodeStyleSettings.java | 1 + .../idea/formatter/KotlinCommonBlock.kt | 45 ++++++++++++++++++- ...KotlinLanguageCodeStyleSettingsProvider.kt | 9 ++++ .../formatter/BinaryExpressions.after.inv.kt | 7 ++- .../formatter/BinaryExpressions.after.kt | 1 - idea/testData/formatter/BinaryExpressions.kt | 1 - .../formatter/IfConditionIndent.after.inv.kt | 18 ++++++++ .../formatter/IfConditionIndent.after.kt | 18 ++++++++ idea/testData/formatter/IfConditionIndent.kt | 18 ++++++++ .../branched/ifWhen/whenToIf/comment.kt.after | 2 +- .../formatter/FormatterTestGenerated.java | 12 +++++ 11 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 idea/testData/formatter/IfConditionIndent.after.inv.kt create mode 100644 idea/testData/formatter/IfConditionIndent.after.kt create mode 100644 idea/testData/formatter/IfConditionIndent.kt diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/core/formatter/KotlinCodeStyleSettings.java b/idea/formatter/src/org/jetbrains/kotlin/idea/core/formatter/KotlinCodeStyleSettings.java index 34c3c528b0d..5e8a7763aa5 100644 --- a/idea/formatter/src/org/jetbrains/kotlin/idea/core/formatter/KotlinCodeStyleSettings.java +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/core/formatter/KotlinCodeStyleSettings.java @@ -45,6 +45,7 @@ public class KotlinCodeStyleSettings extends CustomCodeStyleSettings { public boolean CONTINUATION_INDENT_FOR_EXPRESSION_BODIES = true; public boolean CONTINUATION_INDENT_FOR_CHAINED_CALLS = true; public boolean CONTINUATION_INDENT_IN_SUPERTYPE_LISTS = true; + public boolean CONTINUATION_INDENT_IN_IF_CONDITIONS = true; public int BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES = 0; public int WRAP_EXPRESSION_BODY_FUNCTIONS = 0; public int WRAP_ELVIS_EXPRESSIONS = 1; diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt index fc6a948a4d1..2f7d51e93b3 100644 --- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt @@ -358,12 +358,32 @@ abstract class KotlinCommonBlock( val childrenAlignmentStrategy = getChildrenAlignmentStrategy() val wrappingStrategy = getWrappingStrategy() - return node.children() + val childNodes = if (node.elementType == KtNodeTypes.BINARY_EXPRESSION) { + val binaryExpressionChildren = mutableListOf() + collectBinaryExpressionChildren(node, binaryExpressionChildren) + binaryExpressionChildren.asSequence() + } + else { + node.children() + } + + return childNodes .filter { it.textRange.length > 0 && it.elementType != TokenType.WHITE_SPACE } .map { buildSubBlock(it, childrenAlignmentStrategy, wrappingStrategy ) } .toList() } + private fun collectBinaryExpressionChildren(node: ASTNode, result: MutableList) { + for (child in node.children()) { + if (child.elementType == KtNodeTypes.BINARY_EXPRESSION) { + collectBinaryExpressionChildren(child, result) + } + else { + result.add(child) + } + } + } + private fun getWrappingStrategy(): WrappingStrategy { val commonSettings = settings.kotlinCommonSettings val elementType = node.elementType @@ -574,6 +594,16 @@ private val INDENT_RULES = arrayOf( } .continuationIf(KotlinCodeStyleSettings::CONTINUATION_INDENT_FOR_EXPRESSION_BODIES, indentFirst = true), + strategy("If condition") + .within(KtNodeTypes.CONDITION) + .set { settings -> + val indentType = if (settings.kotlinCustomSettings.CONTINUATION_INDENT_IN_IF_CONDITIONS) + Indent.Type.CONTINUATION + else + Indent.Type.NORMAL + Indent.getIndent(indentType, false, true) + }, + strategy("Property accessor expression body") .within(KtNodeTypes.PROPERTY_ACCESSOR) .forElement { @@ -614,6 +644,7 @@ private val INDENT_RULES = arrayOf( strategy("Binary expressions") .within(BINARY_EXPRESSIONS) + .forElement { node -> !node.suppressBinaryExpressionIndent() } .set(Indent.getContinuationWithoutFirstIndent(false)), strategy("Parenthesized expression") @@ -678,6 +709,18 @@ private fun hasErrorElementBefore(node: ASTNode): Boolean { return lastChild?.elementType == TokenType.ERROR_ELEMENT } +/** + * Suppress indent for binary expressions when there is a block higher in the tree that forces + * its indent to children ('if' condition or elvis). + */ +private fun ASTNode.suppressBinaryExpressionIndent(): Boolean { + var psi = psi.parent as? KtBinaryExpression ?: return false + while (psi.parent is KtBinaryExpression) { + psi = psi.parent as KtBinaryExpression + } + return psi.parent?.node?.elementType == KtNodeTypes.CONDITION || psi.operationToken == KtTokens.ELVIS +} + private fun getAlignmentForChildInParenthesis( shouldAlignChild: Boolean, parameter: IElementType, delimiter: IElementType, shouldAlignParenthesis: Boolean, openBracket: IElementType, closeBracket: IElementType): CommonAlignmentStrategy { diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLanguageCodeStyleSettingsProvider.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLanguageCodeStyleSettingsProvider.kt index 3d013fd1531..70ce104161c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLanguageCodeStyleSettingsProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLanguageCodeStyleSettingsProvider.kt @@ -40,6 +40,10 @@ class KotlinLanguageCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvide is Number -> 0 else -> 1 } + if (i2 > 0 && + i3 < 0) { + return 2 + } return 0 } private fun foo2():Int { @@ -307,6 +311,11 @@ class KotlinLanguageCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvide "Elvis expressions", options = *arrayOf(CodeStyleSettingsCustomizable.WRAP_OPTIONS_FOR_SINGLETON, CodeStyleSettingsCustomizable.WRAP_VALUES_FOR_SINGLETON) ) + showCustomOption( + KotlinCodeStyleSettings::CONTINUATION_INDENT_IN_IF_CONDITIONS, + "Use continuation indent in conditions", + CodeStyleSettingsCustomizable.WRAPPING_IF_STATEMENT + ) } LanguageCodeStyleSettingsProvider.SettingsType.BLANK_LINES_SETTINGS -> { consumer.showStandardOptions( diff --git a/idea/testData/formatter/BinaryExpressions.after.inv.kt b/idea/testData/formatter/BinaryExpressions.after.inv.kt index 7373891e0e6..87b6e721214 100644 --- a/idea/testData/formatter/BinaryExpressions.after.inv.kt +++ b/idea/testData/formatter/BinaryExpressions.after.inv.kt @@ -2,9 +2,9 @@ fun test() { val somelong = 1 + 2 + 3 - 4 - 5 * 6 * - 7 / 8 / - 9 % 10 % - 11 + 7 / 8 / + 9 % 10 % + 11 val withBrackets = 3 + 4 - (5 + @@ -12,4 +12,3 @@ fun test() { } // SET_TRUE: ALIGN_MULTILINE_BINARY_OPERATION -// Strage behaviour for disabled alignment is same to Java diff --git a/idea/testData/formatter/BinaryExpressions.after.kt b/idea/testData/formatter/BinaryExpressions.after.kt index 9a85d7f163f..03c14a3847a 100644 --- a/idea/testData/formatter/BinaryExpressions.after.kt +++ b/idea/testData/formatter/BinaryExpressions.after.kt @@ -12,4 +12,3 @@ fun test() { } // SET_TRUE: ALIGN_MULTILINE_BINARY_OPERATION -// Strage behaviour for disabled alignment is same to Java diff --git a/idea/testData/formatter/BinaryExpressions.kt b/idea/testData/formatter/BinaryExpressions.kt index d025533c751..78bd38d195a 100644 --- a/idea/testData/formatter/BinaryExpressions.kt +++ b/idea/testData/formatter/BinaryExpressions.kt @@ -12,4 +12,3 @@ fun test() { } // SET_TRUE: ALIGN_MULTILINE_BINARY_OPERATION -// Strage behaviour for disabled alignment is same to Java diff --git a/idea/testData/formatter/IfConditionIndent.after.inv.kt b/idea/testData/formatter/IfConditionIndent.after.inv.kt new file mode 100644 index 00000000000..0c3c2e061f5 --- /dev/null +++ b/idea/testData/formatter/IfConditionIndent.after.inv.kt @@ -0,0 +1,18 @@ +fun foo() { + if ("abc" > "def" && + "qqq" < "bbb" && + "ddd" > "efg") { + println("foo") + } +} + +fun foo() { + if ( + "abc" > "def" && + "qqq" < "bbb" && + "ddd" > "efg") { + println("foo") + } +} + +// SET_TRUE: CONTINUATION_INDENT_IN_IF_CONDITIONS diff --git a/idea/testData/formatter/IfConditionIndent.after.kt b/idea/testData/formatter/IfConditionIndent.after.kt new file mode 100644 index 00000000000..aa67cbe3714 --- /dev/null +++ b/idea/testData/formatter/IfConditionIndent.after.kt @@ -0,0 +1,18 @@ +fun foo() { + if ("abc" > "def" && + "qqq" < "bbb" && + "ddd" > "efg") { + println("foo") + } +} + +fun foo() { + if ( + "abc" > "def" && + "qqq" < "bbb" && + "ddd" > "efg") { + println("foo") + } +} + +// SET_TRUE: CONTINUATION_INDENT_IN_IF_CONDITIONS diff --git a/idea/testData/formatter/IfConditionIndent.kt b/idea/testData/formatter/IfConditionIndent.kt new file mode 100644 index 00000000000..da74ea1218a --- /dev/null +++ b/idea/testData/formatter/IfConditionIndent.kt @@ -0,0 +1,18 @@ +fun foo() { + if ("abc" > "def" && + "qqq" < "bbb" && + "ddd" > "efg") { + println("foo") + } +} + +fun foo() { + if ( +"abc" > "def" && +"qqq" < "bbb" && +"ddd" > "efg") { + println("foo") + } +} + +// SET_TRUE: CONTINUATION_INDENT_IN_IF_CONDITIONS diff --git a/idea/testData/intentions/branched/ifWhen/whenToIf/comment.kt.after b/idea/testData/intentions/branched/ifWhen/whenToIf/comment.kt.after index dd97f90352e..5ef39ef2176 100644 --- a/idea/testData/intentions/branched/ifWhen/whenToIf/comment.kt.after +++ b/idea/testData/intentions/branched/ifWhen/whenToIf/comment.kt.after @@ -1,5 +1,5 @@ fun foo(b: Boolean) { if (// comment 1 - b) 1 // comment 2 + b) 1 // comment 2 else 2 } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java b/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java index 45bf0d032d5..5fdf91c9534 100644 --- a/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java @@ -470,6 +470,12 @@ public class FormatterTestGenerated extends AbstractFormatterTest { doTest(fileName); } + @TestMetadata("IfConditionIndent.after.kt") + public void testIfConditionIndent() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/IfConditionIndent.after.kt"); + doTest(fileName); + } + @TestMetadata("IfElseRemoveLineBreak.after.kt") public void testIfElseRemoveLineBreak() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/IfElseRemoveLineBreak.after.kt"); @@ -1316,6 +1322,12 @@ public class FormatterTestGenerated extends AbstractFormatterTest { doTestInverted(fileName); } + @TestMetadata("IfConditionIndent.after.inv.kt") + public void testIfConditionIndent() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/IfConditionIndent.after.inv.kt"); + doTestInverted(fileName); + } + @TestMetadata("IfElseWithTrickyComments.after.inv.kt") public void testIfElseWithTrickyComments() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/IfElseWithTrickyComments.after.inv.kt");