Make add / remove labeled return working for hierarchical blocks
Related to KT-20439
This commit is contained in:
+15
-14
@@ -13,35 +13,36 @@ import org.jetbrains.kotlin.psi.*
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||||
|
|
||||||
class AddLabeledReturnInLambdaIntention : SelfTargetingRangeIntention<KtLambdaExpression>(
|
class AddLabeledReturnInLambdaIntention : SelfTargetingRangeIntention<KtBlockExpression>(
|
||||||
KtLambdaExpression::class.java,
|
KtBlockExpression::class.java,
|
||||||
"Add labeled return to last expression in a lambda"
|
"Add labeled return to last expression in a lambda"
|
||||||
), LowPriorityAction {
|
), LowPriorityAction {
|
||||||
override fun applicabilityRange(element: KtLambdaExpression): TextRange? {
|
override fun applicabilityRange(element: KtBlockExpression): TextRange? {
|
||||||
if (!isApplicableTo(element)) return null
|
if (!isApplicableTo(element)) return null
|
||||||
val labelName = createLabelName(element) ?: return null
|
val labelName = createLabelName(element) ?: return null
|
||||||
text = "Add return@$labelName"
|
text = "Add return@$labelName"
|
||||||
return element.bodyExpression?.statements?.last()?.textRange
|
return element.statements.lastOrNull()?.textRange
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
|
override fun applyTo(element: KtBlockExpression, editor: Editor?) {
|
||||||
if (!isApplicableTo(element)) return
|
if (!isApplicableTo(element)) return
|
||||||
val labelName = createLabelName(element) ?: return
|
val labelName = createLabelName(element) ?: return
|
||||||
val lastStatement = element.bodyExpression?.statements?.last() ?: return
|
val lastStatement = element.statements.lastOrNull() ?: return
|
||||||
val newExpression = KtPsiFactory(element.project).createExpressionByPattern("return@$labelName $0", lastStatement)
|
val newExpression = KtPsiFactory(element.project).createExpressionByPattern("return@$labelName $0", lastStatement)
|
||||||
lastStatement.replace(newExpression)
|
lastStatement.replace(newExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isApplicableTo(element: KtLambdaExpression): Boolean {
|
private fun isApplicableTo(block: KtBlockExpression): Boolean {
|
||||||
val block = element.bodyExpression ?: return false
|
val lastStatement = block.statements.lastOrNull()
|
||||||
val lastStatement = block.statements.last()
|
return lastStatement !is KtReturnExpression && lastStatement?.isUsedAsExpression(lastStatement.analyze()) == true
|
||||||
return lastStatement !is KtReturnExpression && lastStatement.isUsedAsExpression(lastStatement.analyze())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createLabelName(element: KtLambdaExpression): String? {
|
private fun createLabelName(block: KtBlockExpression): String? {
|
||||||
val block = element.bodyExpression ?: return null
|
val lambdaExpression = block.getStrictParentOfType<KtLambdaExpression>() ?: return null
|
||||||
val callExpression = element.getStrictParentOfType<KtCallExpression>() ?: return null
|
val callExpression = lambdaExpression.getStrictParentOfType<KtCallExpression>() ?: return null
|
||||||
val valueArgument = callExpression.valueArguments.findArgumentWithGivenBlock(block) ?: return null
|
val valueArgument = callExpression.valueArguments.find {
|
||||||
|
it.getArgumentExpression()?.unpackFunctionLiteral(allowParentheses = false) === lambdaExpression
|
||||||
|
} ?: return null
|
||||||
val lambdaLabelName = (valueArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
|
val lambdaLabelName = (valueArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
|
||||||
return lambdaLabelName ?: callExpression.getCallNameExpression()?.text
|
return lambdaLabelName ?: callExpression.getCallNameExpression()?.text
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -19,8 +19,11 @@ class RemoveLabeledReturnInLambdaIntention : SelfTargetingIntention<KtReturnExpr
|
|||||||
val labelName = element.getLabelName() ?: return false
|
val labelName = element.getLabelName() ?: return false
|
||||||
val block = element.getStrictParentOfType<KtBlockExpression>() ?: return false
|
val block = element.getStrictParentOfType<KtBlockExpression>() ?: return false
|
||||||
if (block.statements.lastOrNull() != element) return false
|
if (block.statements.lastOrNull() != element) return false
|
||||||
val callExpression = block.getStrictParentOfType<KtCallExpression>() ?: return false
|
val lambdaExpression = block.getStrictParentOfType<KtLambdaExpression>() ?: return false
|
||||||
val lambdaArgument = callExpression.lambdaArguments.findArgumentWithGivenBlock(block) ?: return false
|
val callExpression = lambdaExpression.getStrictParentOfType<KtCallExpression>() ?: return false
|
||||||
|
val lambdaArgument = callExpression.lambdaArguments.find {
|
||||||
|
it.getArgumentExpression().unpackFunctionLiteral(allowParentheses = false) === lambdaExpression
|
||||||
|
} ?: return false
|
||||||
val callName = (lambdaArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
|
val callName = (lambdaArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
|
||||||
?: callExpression.getCallNameExpression()?.text ?: return false
|
?: callExpression.getCallNameExpression()?.text ?: return false
|
||||||
if (labelName != callName) return false
|
if (labelName != callName) return false
|
||||||
|
|||||||
@@ -294,15 +294,3 @@ fun KtCallExpression.isArrayOfMethod(): Boolean {
|
|||||||
return (descriptor.containingDeclaration as? PackageFragmentDescriptor)?.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME &&
|
return (descriptor.containingDeclaration as? PackageFragmentDescriptor)?.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME &&
|
||||||
ARRAY_OF_METHODS.contains(descriptor.name)
|
ARRAY_OF_METHODS.contains(descriptor.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : KtValueArgument> List<T>.findArgumentWithGivenBlock(
|
|
||||||
block: KtBlockExpression
|
|
||||||
): T? = firstOrNull {
|
|
||||||
val argumentExpression = it.getArgumentExpression()
|
|
||||||
val lambda = when (argumentExpression) {
|
|
||||||
is KtLambdaExpression -> argumentExpression
|
|
||||||
is KtLabeledExpression -> argumentExpression.baseExpression as? KtLambdaExpression
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
lambda?.bodyExpression === block
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@find"
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
if (it > 0) {
|
||||||
|
<caret>true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@find"
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
if (it > 0) {
|
||||||
|
return@find true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
if (it > 0) {
|
||||||
|
return@find <caret>true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
if (it > 0) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -390,6 +390,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multipleBlocks.kt")
|
||||||
|
public void testMultipleBlocks() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/multipleBlocks.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("multipleHighOrderFun.kt")
|
@TestMetadata("multipleHighOrderFun.kt")
|
||||||
public void testMultipleHighOrderFun() throws Exception {
|
public void testMultipleHighOrderFun() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/multipleHighOrderFun.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/multipleHighOrderFun.kt");
|
||||||
@@ -13305,6 +13311,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multipleBlocks.kt")
|
||||||
|
public void testMultipleBlocks() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeLabeledReturnInLambda/multipleBlocks.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("normal.kt")
|
@TestMetadata("normal.kt")
|
||||||
public void testNormal() throws Exception {
|
public void testNormal() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeLabeledReturnInLambda/normal.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeLabeledReturnInLambda/normal.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user