diff --git a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties
index cf319bd8ea99..30562e56949a 100644
--- a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties
+++ b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties
@@ -1687,6 +1687,7 @@ convert.to.lambda.expression=Convert to a lambda expression
put.arguments.on.separate.lines=Put arguments on separate lines
put.parameters.on.separate.lines=Put parameters on separate lines
put.expressions.on.separate.lines=Put expressions on separate lines
+put.calls.on.separate.lines=Put calls on separate lines
demorgan.law=DeMorgan Law
replace.with.end.of.line.comment=Replace with end of line comment
convert.collection.constructor.to.function=Convert Collection constructor to function
diff --git a/plugins/kotlin/code-insight/intentions-shared/resources-en/intentionDescriptions/PutCallsOnSeparateLinesIntention/after.kt.template b/plugins/kotlin/code-insight/intentions-shared/resources-en/intentionDescriptions/PutCallsOnSeparateLinesIntention/after.kt.template
new file mode 100644
index 000000000000..875731ef9144
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/resources-en/intentionDescriptions/PutCallsOnSeparateLinesIntention/after.kt.template
@@ -0,0 +1,7 @@
+fun calculate(): Int {
+ return call1().call2()
+ .call3()
+ .call5()
+ .call6()
+ .call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/resources-en/intentionDescriptions/PutCallsOnSeparateLinesIntention/before.kt.template b/plugins/kotlin/code-insight/intentions-shared/resources-en/intentionDescriptions/PutCallsOnSeparateLinesIntention/before.kt.template
new file mode 100644
index 000000000000..4934b2395f31
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/resources-en/intentionDescriptions/PutCallsOnSeparateLinesIntention/before.kt.template
@@ -0,0 +1,3 @@
+fun calculate(): Int {
+ return call1().call2().call3().call5().call6().call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/resources-en/intentionDescriptions/PutCallsOnSeparateLinesIntention/description.html b/plugins/kotlin/code-insight/intentions-shared/resources-en/intentionDescriptions/PutCallsOnSeparateLinesIntention/description.html
new file mode 100644
index 000000000000..c48f7b93847f
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/resources-en/intentionDescriptions/PutCallsOnSeparateLinesIntention/description.html
@@ -0,0 +1,5 @@
+
+
+Formats call chain by placing each call on a separate line.
+
+
\ No newline at end of file
diff --git a/plugins/kotlin/code-insight/intentions-shared/resources/kotlin.code-insight.intentions-shared.xml b/plugins/kotlin/code-insight/intentions-shared/resources/kotlin.code-insight.intentions-shared.xml
index 656e0aca0324..bf997d85ffb7 100644
--- a/plugins/kotlin/code-insight/intentions-shared/resources/kotlin.code-insight.intentions-shared.xml
+++ b/plugins/kotlin/code-insight/intentions-shared/resources/kotlin.code-insight.intentions-shared.xml
@@ -98,6 +98,13 @@
group.names.kotlin
+
+ kotlin
+ org.jetbrains.kotlin.idea.codeInsight.intentions.shared.PutCallsOnSeparateLinesIntention
+ messages.KotlinBundle
+ group.names.kotlin
+
+
kotlin
org.jetbrains.kotlin.idea.codeInsight.intentions.shared.JoinArgumentListIntention
diff --git a/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/PutCallsOnSeparateLinesIntention.kt b/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/PutCallsOnSeparateLinesIntention.kt
new file mode 100644
index 000000000000..42dd30569665
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/PutCallsOnSeparateLinesIntention.kt
@@ -0,0 +1,78 @@
+// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package org.jetbrains.kotlin.idea.codeInsight.intentions.shared
+
+import com.intellij.application.options.CodeStyle
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiWhiteSpace
+import com.intellij.psi.codeStyle.CodeStyleManager
+import com.intellij.psi.impl.source.PostprocessReformattingAspect
+import com.intellij.psi.util.descendants
+import com.intellij.psi.util.parents
+import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
+import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.AbstractKotlinApplicableIntention
+import org.jetbrains.kotlin.idea.codeinsight.api.applicators.KotlinApplicabilityRange
+import org.jetbrains.kotlin.idea.codeinsight.api.applicators.applicabilityRange
+import org.jetbrains.kotlin.idea.formatter.kotlinCommonSettings
+import org.jetbrains.kotlin.psi.KtImportDirective
+import org.jetbrains.kotlin.psi.KtPsiFactory
+import org.jetbrains.kotlin.psi.KtQualifiedExpression
+import org.jetbrains.kotlin.util.takeWhileIsInstance
+
+internal class PutCallsOnSeparateLinesIntention :
+ AbstractKotlinApplicableIntention(KtQualifiedExpression::class) {
+ override fun getActionName(element: KtQualifiedExpression): String = familyName
+ override fun getFamilyName(): String = KotlinBundle.message("put.calls.on.separate.lines")
+
+ override fun apply(element: KtQualifiedExpression, project: Project, editor: Editor?) {
+ val rootQualifierExpression = element.topmostQualifierExpression() ?: return
+ PostprocessReformattingAspect.getInstance(project).disablePostprocessFormattingInside {
+ val psiFactory = KtPsiFactory(project)
+ rootQualifierExpression.visitOperations(transformation = callChainTransformation(element)) { qualifierExpression ->
+ val operationReference = qualifierExpression.operationTokenNode as? PsiElement ?: return@visitOperations
+ val whiteSpace = operationReference.prevSibling as? PsiWhiteSpace
+ when {
+ whiteSpace == null -> qualifierExpression.addBefore(psiFactory.createNewLine(), operationReference)
+ !whiteSpace.textContains('\n') -> whiteSpace.replace(psiFactory.createWhiteSpace("\n${whiteSpace.text}"))
+ }
+ }
+ }
+
+ CodeStyleManager.getInstance(project).reformat(/* element = */ rootQualifierExpression, /* canChangeWhiteSpacesOnly = */ true)
+ }
+
+ override fun getApplicabilityRange(): KotlinApplicabilityRange = applicabilityRange {
+ (it.operationTokenNode as? PsiElement)?.textRangeInParent
+ }
+
+ override fun isApplicableByPsi(element: KtQualifiedExpression): Boolean {
+ val topmostQualifierExpression = element.topmostQualifierExpression() ?: return false
+ if (topmostQualifierExpression.parent is KtImportDirective) return false
+
+ topmostQualifierExpression.visitOperations(transformation = callChainTransformation(element)) {
+ val nextSibling = it.operationTokenNode.treePrev as? PsiWhiteSpace ?: return true
+ if (!nextSibling.textContains('\n')) return true
+ }
+
+ return false
+ }
+
+ private fun callChainTransformation(element: PsiElement): Sequence.() -> Sequence {
+ val wrapFirstCall = CodeStyle.getSettings(element.containingFile).kotlinCommonSettings.WRAP_FIRST_METHOD_IN_CALL_CHAIN
+ return { if (wrapFirstCall) this else drop(1) }
+ }
+}
+
+private inline fun KtQualifiedExpression.visitOperations(
+ transformation: Sequence.() -> Sequence,
+ action: (KtQualifiedExpression) -> Unit,
+) {
+ descendants(childrenFirst = true) { it is KtQualifiedExpression }
+ .filterIsInstance()
+ .transformation()
+ .forEach(action)
+}
+
+private fun KtQualifiedExpression.topmostQualifierExpression(): KtQualifiedExpression? =
+ parents(withSelf = true).takeWhileIsInstance().lastOrNull()
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/k1/test/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/SharedK1IntentionTestGenerated.java b/plugins/kotlin/code-insight/intentions-shared/tests/k1/test/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/SharedK1IntentionTestGenerated.java
index 06b9399bcd77..076262858d9c 100644
--- a/plugins/kotlin/code-insight/intentions-shared/tests/k1/test/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/SharedK1IntentionTestGenerated.java
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/k1/test/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/SharedK1IntentionTestGenerated.java
@@ -445,6 +445,69 @@ public abstract class SharedK1IntentionTestGenerated extends AbstractSharedK1Int
}
}
+ @RunWith(JUnit3RunnerWithInners.class)
+ @TestMetadata("../testData/intentions/putCallsOnSeparateLines")
+ public static class PutCallsOnSeparateLines extends AbstractSharedK1IntentionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+ }
+
+ @TestMetadata("doNotWrapFirstElement.kt")
+ public void testDoNotWrapFirstElement() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement.kt");
+ }
+
+ @TestMetadata("doNotWrapFirstElement2.kt")
+ public void testDoNotWrapFirstElement2() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement2.kt");
+ }
+
+ @TestMetadata("doNotWrapFirstElement3.kt")
+ public void testDoNotWrapFirstElement3() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement3.kt");
+ }
+
+ @TestMetadata("dotChain.kt")
+ public void testDotChain() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/dotChain.kt");
+ }
+
+ @TestMetadata("dotChainWithParenthesis.kt")
+ public void testDotChainWithParenthesis() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/dotChainWithParenthesis.kt");
+ }
+
+ @TestMetadata("import.kt")
+ public void testImport() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/import.kt");
+ }
+
+ @TestMetadata("mixWithMultilineBody.kt")
+ public void testMixWithMultilineBody() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/mixWithMultilineBody.kt");
+ }
+
+ @TestMetadata("mixWithMultilineBodyInFirstPosition.kt")
+ public void testMixWithMultilineBodyInFirstPosition() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/mixWithMultilineBodyInFirstPosition.kt");
+ }
+
+ @TestMetadata("safeChain.kt")
+ public void testSafeChain() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/safeChain.kt");
+ }
+
+ @TestMetadata("safeChainWithLineBreaks.kt")
+ public void testSafeChainWithLineBreaks() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/safeChainWithLineBreaks.kt");
+ }
+
+ @TestMetadata("safeChainWithOneLineBreak.kt")
+ public void testSafeChainWithOneLineBreak() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/safeChainWithOneLineBreak.kt");
+ }
+ }
+
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../testData/intentions/putExpressionsOnSeparateLines")
public static class PutExpressionsOnSeparateLines extends AbstractSharedK1IntentionTest {
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/k2/test/org/jetbrains/kotlin/idea/k2/codeInsight/intentions/shared/SharedK2IntentionTestGenerated.java b/plugins/kotlin/code-insight/intentions-shared/tests/k2/test/org/jetbrains/kotlin/idea/k2/codeInsight/intentions/shared/SharedK2IntentionTestGenerated.java
index 3c7aac7cee07..1f12ddc71740 100644
--- a/plugins/kotlin/code-insight/intentions-shared/tests/k2/test/org/jetbrains/kotlin/idea/k2/codeInsight/intentions/shared/SharedK2IntentionTestGenerated.java
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/k2/test/org/jetbrains/kotlin/idea/k2/codeInsight/intentions/shared/SharedK2IntentionTestGenerated.java
@@ -445,6 +445,69 @@ public abstract class SharedK2IntentionTestGenerated extends AbstractSharedK2Int
}
}
+ @RunWith(JUnit3RunnerWithInners.class)
+ @TestMetadata("../testData/intentions/putCallsOnSeparateLines")
+ public static class PutCallsOnSeparateLines extends AbstractSharedK2IntentionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+ }
+
+ @TestMetadata("doNotWrapFirstElement.kt")
+ public void testDoNotWrapFirstElement() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement.kt");
+ }
+
+ @TestMetadata("doNotWrapFirstElement2.kt")
+ public void testDoNotWrapFirstElement2() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement2.kt");
+ }
+
+ @TestMetadata("doNotWrapFirstElement3.kt")
+ public void testDoNotWrapFirstElement3() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement3.kt");
+ }
+
+ @TestMetadata("dotChain.kt")
+ public void testDotChain() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/dotChain.kt");
+ }
+
+ @TestMetadata("dotChainWithParenthesis.kt")
+ public void testDotChainWithParenthesis() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/dotChainWithParenthesis.kt");
+ }
+
+ @TestMetadata("import.kt")
+ public void testImport() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/import.kt");
+ }
+
+ @TestMetadata("mixWithMultilineBody.kt")
+ public void testMixWithMultilineBody() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/mixWithMultilineBody.kt");
+ }
+
+ @TestMetadata("mixWithMultilineBodyInFirstPosition.kt")
+ public void testMixWithMultilineBodyInFirstPosition() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/mixWithMultilineBodyInFirstPosition.kt");
+ }
+
+ @TestMetadata("safeChain.kt")
+ public void testSafeChain() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/safeChain.kt");
+ }
+
+ @TestMetadata("safeChainWithLineBreaks.kt")
+ public void testSafeChainWithLineBreaks() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/safeChainWithLineBreaks.kt");
+ }
+
+ @TestMetadata("safeChainWithOneLineBreak.kt")
+ public void testSafeChainWithOneLineBreak() throws Exception {
+ runTest("../testData/intentions/putCallsOnSeparateLines/safeChainWithOneLineBreak.kt");
+ }
+ }
+
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../testData/intentions/putExpressionsOnSeparateLines")
public static class PutExpressionsOnSeparateLines extends AbstractSharedK2IntentionTest {
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/.intention b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/.intention
new file mode 100644
index 000000000000..0cb8b9a993d4
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/.intention
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.codeInsight.intentions.shared.PutCallsOnSeparateLinesIntention
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement.kt
new file mode 100644
index 000000000000..bf939449aea5
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement.kt
@@ -0,0 +1,6 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1().call2().call3().call5().ref6.call7()
+}
+
+// SET_TRUE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement.kt.after b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement.kt.after
new file mode 100644
index 000000000000..d29198d34b3c
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement.kt.after
@@ -0,0 +1,11 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1()
+ .call2()
+ .call3()
+ .call5()
+ .ref6
+ .call7()
+}
+
+// SET_TRUE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement2.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement2.kt
new file mode 100644
index 000000000000..c8dba92e5983
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement2.kt
@@ -0,0 +1,6 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1().call2()
+}
+
+// SET_TRUE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement2.kt.after b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement2.kt.after
new file mode 100644
index 000000000000..d974fccc6ff6
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement2.kt.after
@@ -0,0 +1,7 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1()
+ .call2()
+}
+
+// SET_TRUE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement3.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement3.kt
new file mode 100644
index 000000000000..aa472892d70a
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/doNotWrapFirstElement3.kt
@@ -0,0 +1,8 @@
+// IS_APPLICABLE: false
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1()
+ .call2()
+}
+
+// SET_TRUE: WRAP_FIRST_METHOD_IN_CALL_CHAIN
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChain.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChain.kt
new file mode 100644
index 000000000000..d783ca24a57c
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChain.kt
@@ -0,0 +1,4 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1().call2().call3().call5().ref6.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChain.kt.after b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChain.kt.after
new file mode 100644
index 000000000000..9a2ee8e0f24e
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChain.kt.after
@@ -0,0 +1,8 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1().call2()
+ .call3()
+ .call5()
+ .ref6
+ .call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChainWithParenthesis.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChainWithParenthesis.kt
new file mode 100644
index 000000000000..95d6031c1459
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChainWithParenthesis.kt
@@ -0,0 +1,4 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return (call1().call2().call3().call5()).ref6.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChainWithParenthesis.kt.after b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChainWithParenthesis.kt.after
new file mode 100644
index 000000000000..0d8f8e869fc3
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/dotChainWithParenthesis.kt.after
@@ -0,0 +1,6 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return (call1().call2()
+ .call3()
+ .call5()).ref6.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/import.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/import.kt
new file mode 100644
index 000000000000..e838739ccef3
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/import.kt
@@ -0,0 +1,4 @@
+// IS_APPLICABLE: false
+// DISABLE-ERRORS
+
+import a.n.c.d
\ No newline at end of file
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBody.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBody.kt
new file mode 100644
index 000000000000..f54dc51c20c1
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBody.kt
@@ -0,0 +1,6 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1().call2().call3().call5 {
+ doSmth()
+ }?.ref6?.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBody.kt.after b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBody.kt.after
new file mode 100644
index 000000000000..56daa858f1ec
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBody.kt.after
@@ -0,0 +1,10 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1().call2()
+ .call3()
+ .call5 {
+ doSmth()
+ }
+ ?.ref6
+ ?.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBodyInFirstPosition.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBodyInFirstPosition.kt
new file mode 100644
index 000000000000..c50a90a9b60e
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBodyInFirstPosition.kt
@@ -0,0 +1,6 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1().call5 {
+ doSmth()
+ }?.ref6?.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBodyInFirstPosition.kt.after b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBodyInFirstPosition.kt.after
new file mode 100644
index 000000000000..d9b48bd37d17
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/mixWithMultilineBodyInFirstPosition.kt.after
@@ -0,0 +1,8 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1().call5 {
+ doSmth()
+ }
+ ?.ref6
+ ?.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChain.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChain.kt
new file mode 100644
index 000000000000..bec982cf54c4
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChain.kt
@@ -0,0 +1,4 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1()?.call2()?.call3()?.call5()?.ref6?.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChain.kt.after b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChain.kt.after
new file mode 100644
index 000000000000..d173c9d19343
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChain.kt.after
@@ -0,0 +1,8 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1()?.call2()
+ ?.call3()
+ ?.call5()
+ ?.ref6
+ ?.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChainWithLineBreaks.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChainWithLineBreaks.kt
new file mode 100644
index 000000000000..268f24ed953b
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChainWithLineBreaks.kt
@@ -0,0 +1,9 @@
+// IS_APPLICABLE: false
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1()?.call2()
+ ?.call3()
+ ?.call5()
+ ?.ref6
+ ?.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChainWithOneLineBreak.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChainWithOneLineBreak.kt
new file mode 100644
index 000000000000..80478b3d5433
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChainWithOneLineBreak.kt
@@ -0,0 +1,7 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1()?.call2()
+ ?.call3()
+ ?.call5()?.ref6
+ ?.call7()
+}
diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChainWithOneLineBreak.kt.after b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChainWithOneLineBreak.kt.after
new file mode 100644
index 000000000000..d173c9d19343
--- /dev/null
+++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/putCallsOnSeparateLines/safeChainWithOneLineBreak.kt.after
@@ -0,0 +1,8 @@
+// DISABLE-ERRORS
+fun calculate(): Int {
+ return call1()?.call2()
+ ?.call3()
+ ?.call5()
+ ?.ref6
+ ?.call7()
+}
diff --git a/plugins/kotlin/idea/tests/testData/quickfix/createFromUsage/createClass/referenceExpression/classByNestedGroovyQualifier.before.Main.kt b/plugins/kotlin/idea/tests/testData/quickfix/createFromUsage/createClass/referenceExpression/classByNestedGroovyQualifier.before.Main.kt
index e857559c0238..f21e632770fd 100644
--- a/plugins/kotlin/idea/tests/testData/quickfix/createFromUsage/createClass/referenceExpression/classByNestedGroovyQualifier.before.Main.kt
+++ b/plugins/kotlin/idea/tests/testData/quickfix/createFromUsage/createClass/referenceExpression/classByNestedGroovyQualifier.before.Main.kt
@@ -1,5 +1,6 @@
// "Create class 'A'" "false"
// ACTION: Introduce local variable
+// ACTION: Put calls on separate lines
// ACTION: Rename reference
// ERROR: Unresolved reference: A
fun foo() = J.A.B
diff --git a/plugins/kotlin/idea/tests/testData/quickfix/declaringJavaClass/customEnum.kt b/plugins/kotlin/idea/tests/testData/quickfix/declaringJavaClass/customEnum.kt
index 2f59e32c7e6f..ca9e0fcc89e7 100644
--- a/plugins/kotlin/idea/tests/testData/quickfix/declaringJavaClass/customEnum.kt
+++ b/plugins/kotlin/idea/tests/testData/quickfix/declaringJavaClass/customEnum.kt
@@ -1,6 +1,7 @@
// "Replace with 'declaringJavaClass'" "true"
// ACTION: Add method contract to 'getDeclaringClass()'
// ACTION: Introduce local variable
+// ACTION: Put calls on separate lines
// ACTION: Replace with 'declaringJavaClass'
// API_VERSION: 1.7
// WITH_STDLIB
diff --git a/plugins/kotlin/idea/tests/testData/quickfix/declaringJavaClass/customEnum.kt.after b/plugins/kotlin/idea/tests/testData/quickfix/declaringJavaClass/customEnum.kt.after
index 2c64aeaa9115..f59662b718a6 100644
--- a/plugins/kotlin/idea/tests/testData/quickfix/declaringJavaClass/customEnum.kt.after
+++ b/plugins/kotlin/idea/tests/testData/quickfix/declaringJavaClass/customEnum.kt.after
@@ -1,6 +1,7 @@
// "Replace with 'declaringJavaClass'" "true"
// ACTION: Add method contract to 'getDeclaringClass()'
// ACTION: Introduce local variable
+// ACTION: Put calls on separate lines
// ACTION: Replace with 'declaringJavaClass'
// API_VERSION: 1.7
// WITH_STDLIB
diff --git a/plugins/kotlin/idea/tests/testData/quickfix/importAlias/unresolvedReference.kt b/plugins/kotlin/idea/tests/testData/quickfix/importAlias/unresolvedReference.kt
index adb1f67ed90e..f725eefca02c 100644
--- a/plugins/kotlin/idea/tests/testData/quickfix/importAlias/unresolvedReference.kt
+++ b/plugins/kotlin/idea/tests/testData/quickfix/importAlias/unresolvedReference.kt
@@ -1,4 +1,5 @@
// "Introduce import alias" "false"
+// ACTION: Put calls on separate lines
// WITH_STDLIB
// ERROR: Overload resolution ambiguity:
public inline fun Iterable.forEach(action: (TypeVariable(T)) -> Unit): Unit defined in kotlin.collections
public inline fun Map.forEach(action: (Map.Entry) -> Unit): Unit defined in kotlin.collections
// ERROR: Unresolved reference: a
diff --git a/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/chainedUnsafeCall.kt b/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/chainedUnsafeCall.kt
index f29732dc983e..2c3769a9e6dd 100644
--- a/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/chainedUnsafeCall.kt
+++ b/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/chainedUnsafeCall.kt
@@ -1,6 +1,7 @@
// "Surround with null check" "false"
// ACTION: Add non-null asserted (!!) call
// ACTION: Introduce local variable
+// ACTION: Put calls on separate lines
// ACTION: Replace with safe (?.) call
// ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Int?
diff --git a/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/chainedUnsafeCall.kt b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/chainedUnsafeCall.kt
index c6f27e5cef01..1e5daf919b10 100644
--- a/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/chainedUnsafeCall.kt
+++ b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/chainedUnsafeCall.kt
@@ -2,6 +2,7 @@
// WITH_STDLIB
// ACTION: Add non-null asserted (!!) call
// ACTION: Introduce local variable
+// ACTION: Put calls on separate lines
// ACTION: Replace with safe (?.) call
// ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Int?
diff --git a/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/nullableReceiver.kt b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/nullableReceiver.kt
index 222423ff6773..657591b31bd3 100644
--- a/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/nullableReceiver.kt
+++ b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/nullableReceiver.kt
@@ -2,6 +2,7 @@
// ACTION: Add non-null asserted (!!) call
// ACTION: Convert to run
// ACTION: Convert to with
+// ACTION: Put calls on separate lines
// ACTION: Replace with safe (?.) call
// ACTION: Surround with null check
// ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type B?