Skip to content

Commit 59f6dc0

Browse files
shirajimglukhikh
authored andcommitted
Implement intention to add labeled return to last expression in a lambda
So #KT-20439 Fixed
1 parent 769e285 commit 59f6dc0

26 files changed

+311
-11
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fun foo() {
2+
listOf(1,2,3).find {
3+
<spot>return@find</spot> true
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fun foo() {
2+
listOf(1,2,3).find {
3+
true
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<html>
2+
<body>
3+
This intention adds labeled return to last expression in a lambda.
4+
</body>
5+
</html>

idea/src/META-INF/plugin.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,11 @@
15621562
<category>Kotlin</category>
15631563
</intentionAction>
15641564

1565+
<intentionAction>
1566+
<className>org.jetbrains.kotlin.idea.intentions.AddLabeledReturnInLambdaIntention</className>
1567+
<category>Kotlin</category>
1568+
</intentionAction>
1569+
15651570
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
15661571
displayName="Object literal can be converted to lambda"
15671572
groupPath="Kotlin"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
3+
* that can be found in the license/LICENSE.txt file.
4+
*/
5+
6+
package org.jetbrains.kotlin.idea.intentions
7+
8+
import com.intellij.codeInsight.intention.LowPriorityAction
9+
import com.intellij.openapi.editor.Editor
10+
import com.intellij.openapi.util.TextRange
11+
import org.jetbrains.kotlin.idea.caches.resolve.analyze
12+
import org.jetbrains.kotlin.psi.*
13+
import org.jetbrains.kotlin.psi.psiUtil.*
14+
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
15+
16+
class AddLabeledReturnInLambdaIntention : SelfTargetingRangeIntention<KtLambdaExpression>(
17+
KtLambdaExpression::class.java,
18+
"Add labeled return to last expression in a lambda"
19+
), LowPriorityAction {
20+
override fun applicabilityRange(element: KtLambdaExpression): TextRange? {
21+
if (!isApplicableTo(element)) return null
22+
val labelName = createLabelName(element) ?: return null
23+
text = "Add return@$labelName"
24+
return element.bodyExpression?.statements?.last()?.textRange
25+
}
26+
27+
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
28+
if (!isApplicableTo(element)) return
29+
val labelName = createLabelName(element) ?: return
30+
val lastStatement = element.bodyExpression?.statements?.last() ?: return
31+
val newExpression = KtPsiFactory(element.project).createExpressionByPattern("return@$labelName $0", lastStatement)
32+
lastStatement.replace(newExpression)
33+
}
34+
35+
private fun isApplicableTo(element: KtLambdaExpression): Boolean {
36+
val block = element.bodyExpression ?: return false
37+
val lastStatement = block.statements.last()
38+
return lastStatement !is KtReturnExpression && lastStatement.isUsedAsExpression(lastStatement.analyze())
39+
}
40+
41+
private fun createLabelName(element: KtLambdaExpression): String? {
42+
val block = element.bodyExpression ?: return null
43+
val callExpression = element.getStrictParentOfType<KtCallExpression>() ?: return null
44+
val valueArgument = callExpression.valueArguments.findArgumentWithGivenBlock(block) ?: return null
45+
val lambdaLabelName = (valueArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
46+
return lambdaLabelName ?: callExpression.getCallNameExpression()?.text
47+
}
48+
}

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import com.intellij.codeInsight.intention.LowPriorityAction
99
import com.intellij.openapi.editor.Editor
1010
import org.jetbrains.kotlin.psi.*
1111
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
12-
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
1312
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
1413

1514
class RemoveLabeledReturnInLambdaIntention : SelfTargetingIntention<KtReturnExpression>(
@@ -21,15 +20,7 @@ class RemoveLabeledReturnInLambdaIntention : SelfTargetingIntention<KtReturnExpr
2120
val block = element.getStrictParentOfType<KtBlockExpression>() ?: return false
2221
if (block.statements.lastOrNull() != element) return false
2322
val callExpression = block.getStrictParentOfType<KtCallExpression>() ?: return false
24-
val lambdaArgument = callExpression.lambdaArguments.firstOrNull {
25-
val argumentExpression = it.getArgumentExpression()
26-
val lambda = when (argumentExpression) {
27-
is KtLambdaExpression -> argumentExpression
28-
is KtLabeledExpression -> argumentExpression.baseExpression as? KtLambdaExpression
29-
else -> null
30-
}
31-
lambda?.bodyExpression === block
32-
} ?: return false
23+
val lambdaArgument = callExpression.lambdaArguments.findArgumentWithGivenBlock(block) ?: return false
3324
val callName = (lambdaArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
3425
?: callExpression.getCallNameExpression()?.text ?: return false
3526
if (labelName != callName) return false

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.name.Name
3232
import org.jetbrains.kotlin.psi.*
3333
import org.jetbrains.kotlin.resolve.BindingContext
3434
import org.jetbrains.kotlin.resolve.CollectionLiteralResolver
35-
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
3635
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
3736
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
3837
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -295,3 +294,15 @@ fun KtCallExpression.isArrayOfMethod(): Boolean {
295294
return (descriptor.containingDeclaration as? PackageFragmentDescriptor)?.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME &&
296295
ARRAY_OF_METHODS.contains(descriptor.name)
297296
}
297+
298+
fun <T : KtValueArgument> List<T>.findArgumentWithGivenBlock(
299+
block: KtBlockExpression
300+
): T? = firstOrNull {
301+
val argumentExpression = it.getArgumentExpression()
302+
val lambda = when (argumentExpression) {
303+
is KtLambdaExpression -> argumentExpression
304+
is KtLabeledExpression -> argumentExpression.baseExpression as? KtLambdaExpression
305+
else -> null
306+
}
307+
lambda?.bodyExpression === block
308+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.jetbrains.kotlin.idea.intentions.AddLabeledReturnInLambdaIntention
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@find"
3+
4+
fun foo() {
5+
listOf(1,2,3).find({
6+
<caret>true
7+
})
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@find"
3+
4+
fun foo() {
5+
listOf(1,2,3).find({
6+
return@find true
7+
})
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@label"
3+
4+
fun foo() {
5+
listOf(1,2,3).find label@{
6+
<caret>true
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@label"
3+
4+
fun foo() {
5+
listOf(1,2,3).find label@{
6+
return@label true
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// IS_APPLICABLE: false
2+
// WITH_RUNTIME
3+
4+
fun foo() {
5+
for (i in 10..100) {
6+
<caret>true
7+
}
8+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@bar"
3+
4+
private fun <T> List<T>.bar(a: (T) -> Boolean, b: (T) -> Boolean) {}
5+
6+
fun foo() {
7+
listOf(1,2,3).bar({
8+
true
9+
}) {
10+
<caret>true
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@bar"
3+
4+
private fun <T> List<T>.bar(a: (T) -> Boolean, b: (T) -> Boolean) {}
5+
6+
fun foo() {
7+
listOf(1,2,3).bar({
8+
true
9+
}) {
10+
return@bar true
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@bar"
3+
4+
private fun <T> List<T>.bar(a: (T) -> Boolean, b: (T) -> Boolean) {}
5+
6+
fun foo() {
7+
listOf(1,2,3).bar({
8+
<caret>true
9+
}) {
10+
true
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@bar"
3+
4+
private fun <T> List<T>.bar(a: (T) -> Boolean, b: (T) -> Boolean) {}
5+
6+
fun foo() {
7+
listOf(1,2,3).bar({
8+
return@bar true
9+
}) {
10+
true
11+
}
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@find"
3+
4+
fun foo() {
5+
listOf(1,2,3).find {
6+
<caret>true
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@find"
3+
4+
fun foo() {
5+
listOf(1,2,3).find {
6+
return@find true
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// IS_APPLICABLE: false
2+
// WITH_RUNTIME
3+
4+
fun foo() {
5+
listOf(1,2,3).find {
6+
<caret>1
7+
true
8+
}
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@find"
3+
4+
fun foo(): Int? {
5+
return listOf(1,2,3).find {
6+
<caret>true
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// WITH_RUNTIME
2+
// INTENTION_TEXT: "Add return@find"
3+
4+
fun foo(): Int? {
5+
return listOf(1,2,3).find {
6+
return@find true
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// IS_APPLICABLE: false
2+
// WITH_RUNTIME
3+
4+
fun foo() {
5+
listOf(1,2,3).forEach {
6+
<caret>true
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// IS_APPLICABLE: false
2+
// WITH_RUNTIME
3+
4+
fun foo() {
5+
listOf(1,2,3).find {
6+
return@find <caret>true
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// IS_APPLICABLE: false
2+
// WITH_RUNTIME
3+
4+
fun foo(): Boolean {
5+
listOf(1,2,3).find {
6+
return <caret>true
7+
}
8+
return false
9+
}

0 commit comments

Comments
 (0)