Skip to content

Commit 7f880bf

Browse files
t-kameyamamglukhikh
authored andcommitted
Replace with for-each: add new-line if necessary #KT-15858 Fixed
1 parent ae7f60a commit 7f880bf

File tree

10 files changed

+102
-3
lines changed

10 files changed

+102
-3
lines changed

idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToForEachFunctionCallIntention.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,22 @@
1717
package org.jetbrains.kotlin.idea.intentions
1818

1919
import com.intellij.openapi.editor.Editor
20+
import com.intellij.psi.PsiComment
2021
import com.intellij.psi.PsiElement
22+
import org.jetbrains.kotlin.idea.refactoring.getLineNumber
2123
import org.jetbrains.kotlin.idea.util.CommentSaver
24+
import org.jetbrains.kotlin.lexer.KtTokens
2225
import org.jetbrains.kotlin.psi.*
2326
import org.jetbrains.kotlin.psi.psiUtil.contentRange
2427
import org.jetbrains.kotlin.psi.psiUtil.endOffset
2528
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
2629
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
2730
import java.util.*
2831

29-
class ConvertToForEachFunctionCallIntention : SelfTargetingIntention<KtForExpression>(KtForExpression::class.java, "Replace with a 'forEach' function call") {
32+
class ConvertToForEachFunctionCallIntention : SelfTargetingIntention<KtForExpression>(
33+
KtForExpression::class.java,
34+
"Replace with a 'forEach' function call"
35+
) {
3036
override fun isApplicableTo(element: KtForExpression, caretOffset: Int): Boolean {
3137
val rParen = element.rightParenthesis ?: return false
3238
if (caretOffset > rParen.endOffset) return false // available only on the loop header, not in the body
@@ -41,11 +47,14 @@ class ConvertToForEachFunctionCallIntention : SelfTargetingIntention<KtForExpres
4147
val body = element.body!!
4248
val loopParameter = element.loopParameter!!
4349

44-
val functionBodyArgument: Any = (body as? KtBlockExpression)?.contentRange() ?: body
50+
val blockExpression = body as? KtBlockExpression
51+
val functionBodyArgument: Any = blockExpression?.contentRange() ?: body
52+
val pattern = if (needLineBreaks(blockExpression)) "$0.forEach{$1->\n$2\n}" else "$0.forEach{$1->$2}"
4553

4654
val psiFactory = KtPsiFactory(element)
4755
val foreachExpression = psiFactory.createExpressionByPattern(
48-
"$0.forEach{$1->$2}", element.loopRange!!, loopParameter, functionBodyArgument)
56+
pattern, element.loopRange!!, loopParameter, functionBodyArgument
57+
)
4958
val result = element.replace(foreachExpression) as KtElement
5059

5160
result.findDescendantOfType<KtFunctionLiteral>()!!.getContinuesWithLabel(labelName).forEach {
@@ -55,6 +64,14 @@ class ConvertToForEachFunctionCallIntention : SelfTargetingIntention<KtForExpres
5564
commentSaver.restore(result)
5665
}
5766

67+
private fun needLineBreaks(blockExpression: KtBlockExpression?): Boolean {
68+
val contentRange = blockExpression?.contentRange() ?: return false
69+
if ((contentRange.last as? PsiComment)?.tokenType != KtTokens.EOL_COMMENT) return false
70+
val statements = blockExpression.statements
71+
val fistStatementLine = statements.firstOrNull()?.getLineNumber()
72+
return statements.all { it.getLineNumber() == fistStatementLine }
73+
}
74+
5875
private fun KtElement.getContinuesWithLabel(labelName: String?): List<KtContinueExpression> {
5976
val continueElements = ArrayList<KtContinueExpression>()
6077

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
fun foo() {}
3+
4+
fun test() {
5+
<caret>for (l in listOf(1, 2)) {
6+
/* comment */
7+
}
8+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// WITH_RUNTIME
2+
fun foo() {}
3+
4+
fun test() {
5+
listOf(1, 2).forEach { l -> /* comment */ }
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// WITH_RUNTIME
2+
fun test() {
3+
<caret>for (l in listOf(1, 2)) {
4+
// comment
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// WITH_RUNTIME
2+
fun test() {
3+
listOf(1, 2).forEach { l ->
4+
// comment
5+
}
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
fun foo() {}
3+
4+
fun test() {
5+
<caret>for (l in listOf(1, 2)) {
6+
foo() // comment
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
fun foo() {}
3+
4+
fun test() {
5+
listOf(1, 2).forEach { l ->
6+
foo() // comment
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
fun foo() {}
3+
4+
fun test() {
5+
<caret>for (l in listOf(1, 2)) {
6+
foo(); foo() // comment
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
fun foo() {}
3+
4+
fun test() {
5+
listOf(1, 2).forEach { l ->
6+
foo(); foo() // comment
7+
}
8+
}

idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)