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 5e03ef5422a..2c4ac23db0f 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 @@ -42,6 +42,7 @@ public class KotlinCodeStyleSettings extends CustomCodeStyleSettings { public boolean IMPORT_NESTED_CLASSES = false; public boolean CONTINUATION_INDENT_IN_PARAMETER_LISTS = true; public boolean CONTINUATION_INDENT_FOR_EXPRESSION_BODIES = true; + public boolean CONTINUATION_INDENT_FOR_CHAINED_CALLS = true; public KotlinCodeStyleSettings(CodeStyleSettings container) { super("JetCodeStyleSettings", container); 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 3e7fdeed873..5152908d29b 100644 --- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt @@ -94,10 +94,14 @@ abstract class KotlinCommonBlock( // relative to it when it starts from new line (see Indent javadoc). val operationBlock = nodeSubBlocks[operationBlockIndex] + val indent = if (settings.kotlinSettings.CONTINUATION_INDENT_FOR_CHAINED_CALLS) + Indent.getContinuationWithoutFirstIndent() + else + Indent.getNormalIndent() val operationSyntheticBlock = SyntheticKotlinBlock( (operationBlock as ASTBlock).node, nodeSubBlocks.subList(operationBlockIndex, nodeSubBlocks.size), - null, operationBlock.getIndent(), null, spacingBuilder) { createSyntheticSpacingNodeBlock(it) } + null, indent, null, spacingBuilder) { createSyntheticSpacingNodeBlock(it) } nodeSubBlocks = ArrayList(nodeSubBlocks.subList(0, operationBlockIndex)) nodeSubBlocks.add(operationSyntheticBlock) @@ -382,7 +386,14 @@ private val INDENT_RULES = arrayOf( strategy("Chained calls") .within(KtNodeTypes.DOT_QUALIFIED_EXPRESSION, KtNodeTypes.SAFE_ACCESS_EXPRESSION) - .set(Indent.getContinuationWithoutFirstIndent(false)), + .notForType(KtTokens.DOT, KtTokens.SAFE_ACCESS) + .forElement { it.treeParent.firstChildNode != it } + .set { settings -> + if (settings.kotlinSettings.CONTINUATION_INDENT_FOR_CHAINED_CALLS) + Indent.getContinuationWithoutFirstIndent() + else + Indent.getNormalIndent() + }, strategy("Colon of delegation list") .within(KtNodeTypes.CLASS, KtNodeTypes.OBJECT_DECLARATION) diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinIndentOptionsEditor.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinIndentOptionsEditor.kt index 2155df598f2..0f2f9ed30c8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinIndentOptionsEditor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinIndentOptionsEditor.kt @@ -26,11 +26,13 @@ import javax.swing.JCheckBox class KotlinIndentOptionsEditor : SmartIndentOptionsEditor() { private val useContinuationIndentInParameterList = JCheckBox("Use continuation indent in parameter lists") private val useContinuationIndentForExpressionBodies = JCheckBox("Use continuation indent for expression bodies") + private val useContinuationIndentForChainedCalls = JCheckBox("Use continuation indent for chained calls") override fun addComponents() { super.addComponents() add(useContinuationIndentInParameterList) add(useContinuationIndentForExpressionBodies) + add(useContinuationIndentForChainedCalls) } override fun isModified(settings: CodeStyleSettings, options: CommonCodeStyleSettings.IndentOptions): Boolean { @@ -40,6 +42,8 @@ class KotlinIndentOptionsEditor : SmartIndentOptionsEditor() { kotlinSettings.CONTINUATION_INDENT_IN_PARAMETER_LISTS) isModified = isModified || IndentOptionsEditor.isFieldModified(useContinuationIndentForExpressionBodies, kotlinSettings.CONTINUATION_INDENT_FOR_EXPRESSION_BODIES) + isModified = isModified || IndentOptionsEditor.isFieldModified(useContinuationIndentForChainedCalls, + kotlinSettings.CONTINUATION_INDENT_FOR_CHAINED_CALLS) return isModified } @@ -48,6 +52,7 @@ class KotlinIndentOptionsEditor : SmartIndentOptionsEditor() { val kotlinSettings = settings.getCustomSettings(KotlinCodeStyleSettings::class.java) kotlinSettings.CONTINUATION_INDENT_IN_PARAMETER_LISTS = useContinuationIndentInParameterList.isSelected kotlinSettings.CONTINUATION_INDENT_FOR_EXPRESSION_BODIES = useContinuationIndentForExpressionBodies.isSelected + kotlinSettings.CONTINUATION_INDENT_FOR_CHAINED_CALLS = useContinuationIndentForChainedCalls.isSelected } override fun reset(settings: CodeStyleSettings, options: CommonCodeStyleSettings.IndentOptions) { @@ -55,11 +60,13 @@ class KotlinIndentOptionsEditor : SmartIndentOptionsEditor() { val kotlinSettings = settings.getCustomSettings(KotlinCodeStyleSettings::class.java) useContinuationIndentInParameterList.isSelected = kotlinSettings.CONTINUATION_INDENT_IN_PARAMETER_LISTS useContinuationIndentForExpressionBodies.isSelected = kotlinSettings.CONTINUATION_INDENT_FOR_EXPRESSION_BODIES + useContinuationIndentForChainedCalls.isSelected = kotlinSettings.CONTINUATION_INDENT_FOR_CHAINED_CALLS } override fun setEnabled(enabled: Boolean) { super.setEnabled(enabled) useContinuationIndentInParameterList.isEnabled = enabled useContinuationIndentForExpressionBodies.isEnabled = enabled + useContinuationIndentForChainedCalls.isEnabled = enabled } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLanguageCodeStyleSettingsProvider.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLanguageCodeStyleSettingsProvider.kt index af2d6e0b70b..d554289d835 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLanguageCodeStyleSettingsProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLanguageCodeStyleSettingsProvider.kt @@ -125,6 +125,8 @@ class KotlinLanguageCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvide foo: String, bar: String ) { + foo + .length } fun expressionBodyMethod() = diff --git a/idea/testData/formatter/ConsecutiveSafeCallsIndent.after.inv.kt b/idea/testData/formatter/ConsecutiveSafeCallsIndent.after.inv.kt new file mode 100644 index 00000000000..cc7addbff56 --- /dev/null +++ b/idea/testData/formatter/ConsecutiveSafeCallsIndent.after.inv.kt @@ -0,0 +1,12 @@ +class Some { + fun some(): Some? = this +} + +public fun bar(): String? = + Some() + ?.some() + ?.some() + ?.some()!! + .toString() + +// SET_TRUE: CONTINUATION_INDENT_FOR_CHAINED_CALLS diff --git a/idea/testData/formatter/ConsecutiveSafeCallsIndent.after.kt b/idea/testData/formatter/ConsecutiveSafeCallsIndent.after.kt index dc510293495..a0e3293a173 100644 --- a/idea/testData/formatter/ConsecutiveSafeCallsIndent.after.kt +++ b/idea/testData/formatter/ConsecutiveSafeCallsIndent.after.kt @@ -9,3 +9,4 @@ public fun bar(): String? = ?.some()!! .toString() +// SET_TRUE: CONTINUATION_INDENT_FOR_CHAINED_CALLS diff --git a/idea/testData/formatter/ConsecutiveSafeCallsIndent.kt b/idea/testData/formatter/ConsecutiveSafeCallsIndent.kt index da7af9c7cf7..7cc94bfc23f 100644 --- a/idea/testData/formatter/ConsecutiveSafeCallsIndent.kt +++ b/idea/testData/formatter/ConsecutiveSafeCallsIndent.kt @@ -9,3 +9,4 @@ Some() ?.some()!! .toString() +// SET_TRUE: CONTINUATION_INDENT_FOR_CHAINED_CALLS diff --git a/idea/testData/formatter/ContinuationIndentForChainedCalls.after.inv.kt b/idea/testData/formatter/ContinuationIndentForChainedCalls.after.inv.kt new file mode 100644 index 00000000000..020bc2a46b9 --- /dev/null +++ b/idea/testData/formatter/ContinuationIndentForChainedCalls.after.inv.kt @@ -0,0 +1,4 @@ +val x = "abc" + .length + +// SET_TRUE: CONTINUATION_INDENT_FOR_CHAINED_CALLS diff --git a/idea/testData/formatter/ContinuationIndentForChainedCalls.after.kt b/idea/testData/formatter/ContinuationIndentForChainedCalls.after.kt new file mode 100644 index 00000000000..e85922fc8d7 --- /dev/null +++ b/idea/testData/formatter/ContinuationIndentForChainedCalls.after.kt @@ -0,0 +1,4 @@ +val x = "abc" + .length + +// SET_TRUE: CONTINUATION_INDENT_FOR_CHAINED_CALLS diff --git a/idea/testData/formatter/ContinuationIndentForChainedCalls.kt b/idea/testData/formatter/ContinuationIndentForChainedCalls.kt new file mode 100644 index 00000000000..e85922fc8d7 --- /dev/null +++ b/idea/testData/formatter/ContinuationIndentForChainedCalls.kt @@ -0,0 +1,4 @@ +val x = "abc" + .length + +// SET_TRUE: CONTINUATION_INDENT_FOR_CHAINED_CALLS diff --git a/idea/testData/formatter/FunctionLiteralsInChainCalls.after.inv.kt b/idea/testData/formatter/FunctionLiteralsInChainCalls.after.inv.kt new file mode 100644 index 00000000000..7fe6d68c9e8 --- /dev/null +++ b/idea/testData/formatter/FunctionLiteralsInChainCalls.after.inv.kt @@ -0,0 +1,46 @@ +import java.util.ArrayList + +fun test() { + val abc = ArrayList() + .map { + it * 2 + } + .filter { + it > 4 + } +} + +fun test1() { + val abc = ArrayList() + .map({ + it * 2 + }) + .filter({ + it > 4 + }) +} + +fun test2() { + val abc = ArrayList() + .map { + it * 2 + } +} + +fun test3() { + val abc = ArrayList().map { + it * 2 + } +} + +fun testWithComments() { + val abc = ArrayList() +// .map { +// it * 2 +// } + .filter { + it > 4 + } +} + +// SET_TRUE: CONTINUATION_INDENT_FOR_CHAINED_CALLS diff --git a/idea/testData/formatter/FunctionLiteralsInChainCalls.after.kt b/idea/testData/formatter/FunctionLiteralsInChainCalls.after.kt index 3efb11e9125..5ec4362b440 100644 --- a/idea/testData/formatter/FunctionLiteralsInChainCalls.after.kt +++ b/idea/testData/formatter/FunctionLiteralsInChainCalls.after.kt @@ -43,3 +43,4 @@ fun testWithComments() { } } +// SET_TRUE: CONTINUATION_INDENT_FOR_CHAINED_CALLS diff --git a/idea/testData/formatter/FunctionLiteralsInChainCalls.kt b/idea/testData/formatter/FunctionLiteralsInChainCalls.kt index a453574a34f..39f7c662201 100644 --- a/idea/testData/formatter/FunctionLiteralsInChainCalls.kt +++ b/idea/testData/formatter/FunctionLiteralsInChainCalls.kt @@ -42,3 +42,5 @@ fun testWithComments() { it > 4 } } + +// SET_TRUE: CONTINUATION_INDENT_FOR_CHAINED_CALLS diff --git a/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java b/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java index 22f93f39ed4..8f0dbe47df3 100644 --- a/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java @@ -176,6 +176,12 @@ public class FormatterTestGenerated extends AbstractFormatterTest { doTest(fileName); } + @TestMetadata("ContinuationIndentForChainedCalls.after.kt") + public void testContinuationIndentForChainedCalls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/ContinuationIndentForChainedCalls.after.kt"); + doTest(fileName); + } + @TestMetadata("ContinuationIndentInParameterLists.after.kt") public void testContinuationIndentInParameterLists() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/ContinuationIndentInParameterLists.after.kt"); @@ -1064,6 +1070,18 @@ public class FormatterTestGenerated extends AbstractFormatterTest { doTestInverted(fileName); } + @TestMetadata("ConsecutiveSafeCallsIndent.after.inv.kt") + public void testConsecutiveSafeCallsIndent() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/ConsecutiveSafeCallsIndent.after.inv.kt"); + doTestInverted(fileName); + } + + @TestMetadata("ContinuationIndentForChainedCalls.after.inv.kt") + public void testContinuationIndentForChainedCalls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/ContinuationIndentForChainedCalls.after.inv.kt"); + doTestInverted(fileName); + } + @TestMetadata("ContinuationIndentForExpressionBodies.after.inv.kt") public void testContinuationIndentForExpressionBodies() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/ContinuationIndentForExpressionBodies.after.inv.kt"); @@ -1130,6 +1148,12 @@ public class FormatterTestGenerated extends AbstractFormatterTest { doTestInverted(fileName); } + @TestMetadata("FunctionLiteralsInChainCalls.after.inv.kt") + public void testFunctionLiteralsInChainCalls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/FunctionLiteralsInChainCalls.after.inv.kt"); + doTestInverted(fileName); + } + @TestMetadata("FunctionalType.after.inv.kt") public void testFunctionalType() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/formatter/FunctionalType.after.inv.kt");