Better implementation of "Move lambda outside parenthesis"
This commit is contained in:
@@ -21,19 +21,18 @@ import com.intellij.psi.PsiWhiteSpace
|
|||||||
|
|
||||||
public class JetFunctionLiteralArgument(node: ASTNode) : JetValueArgument(node), FunctionLiteralArgument {
|
public class JetFunctionLiteralArgument(node: ASTNode) : JetValueArgument(node), FunctionLiteralArgument {
|
||||||
|
|
||||||
private fun assertFL() = throw AssertionError("Function literal argument doesn't contain function literal expression: " +
|
override fun getArgumentExpression() = super<JetValueArgument>.getArgumentExpression()!!
|
||||||
"${super<JetValueArgument>.getArgumentExpression()?.getText()} (it should be guaranteed by parser)")
|
|
||||||
|
|
||||||
override fun getArgumentExpression() = super<JetValueArgument>.getArgumentExpression() ?: assertFL()
|
override fun getFunctionLiteral(): JetFunctionLiteralExpression = getArgumentExpression().unpackFunctionLiteral()!!
|
||||||
|
|
||||||
override fun getFunctionLiteral(): JetFunctionLiteralExpression = unpackFunctionLiteral(getArgumentExpression())
|
|
||||||
|
|
||||||
private fun unpackFunctionLiteral(expression: JetExpression?): JetFunctionLiteralExpression =
|
|
||||||
when (expression) {
|
|
||||||
is JetFunctionLiteralExpression -> expression
|
|
||||||
is JetLabeledExpression -> unpackFunctionLiteral(expression.getBaseExpression())
|
|
||||||
is JetAnnotatedExpression -> unpackFunctionLiteral(expression.getBaseExpression())
|
|
||||||
else -> assertFL()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun JetExpression.unpackFunctionLiteral(): JetFunctionLiteralExpression? {
|
||||||
|
return when (this) {
|
||||||
|
is JetFunctionLiteralExpression -> this
|
||||||
|
is JetLabeledExpression -> getBaseExpression()?.unpackFunctionLiteral()
|
||||||
|
is JetAnnotatedExpression -> getBaseExpression()?.unpackFunctionLiteral()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,10 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args:
|
|||||||
CodeEditUtil.setNodeGeneratedRecursively(expression.getNode(), false)
|
CodeEditUtil.setNodeGeneratedRecursively(expression.getNode(), false)
|
||||||
|
|
||||||
for ((pointer, n) in pointers) {
|
for ((pointer, n) in pointers) {
|
||||||
val element = pointer.getElement()!!
|
var element = pointer.getElement()!!
|
||||||
|
if (element is JetFunctionLiteral) {
|
||||||
|
element = element.getParent() as JetFunctionLiteralExpression
|
||||||
|
}
|
||||||
element.replace(args[n] as PsiElement)
|
element.replace(args[n] as PsiElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-12
@@ -20,7 +20,7 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveLambdaOutsideParentheses
|
import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveFunctionLiteralOutsideParentheses
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
@@ -29,9 +29,10 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
|||||||
|
|
||||||
public class MoveLambdaOutsideParenthesesIntention : JetSelfTargetingIntention<JetCallExpression>(javaClass(), "Move lambda argument out of parentheses") {
|
public class MoveLambdaOutsideParenthesesIntention : JetSelfTargetingIntention<JetCallExpression>(javaClass(), "Move lambda argument out of parentheses") {
|
||||||
override fun isApplicableTo(element: JetCallExpression, caretOffset: Int): Boolean {
|
override fun isApplicableTo(element: JetCallExpression, caretOffset: Int): Boolean {
|
||||||
val argument = element.getValueArgumentsInParentheses().lastOrNull() ?: return false
|
if (element.getFunctionLiteralArguments().isNotEmpty()) return false
|
||||||
|
val argument = element.getValueArguments().lastOrNull() ?: return false
|
||||||
val expression = argument.getArgumentExpression() ?: return false
|
val expression = argument.getArgumentExpression() ?: return false
|
||||||
val functionLiteral = getFunctionLiteral(expression) ?: return false
|
val functionLiteral = expression.unpackFunctionLiteral() ?: return false
|
||||||
|
|
||||||
val callee = element.getCalleeExpression()
|
val callee = element.getCalleeExpression()
|
||||||
if (callee is JetSimpleNameExpression) {
|
if (callee is JetSimpleNameExpression) {
|
||||||
@@ -54,15 +55,7 @@ public class MoveLambdaOutsideParenthesesIntention : JetSelfTargetingIntention<J
|
|||||||
return !bodyRange.containsInside(caretOffset)
|
return !bodyRange.containsInside(caretOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFunctionLiteral(expression: JetExpression?): JetFunctionLiteral? {
|
|
||||||
return when (expression) {
|
|
||||||
is JetFunctionLiteralExpression -> expression.getFunctionLiteral()
|
|
||||||
is JetLabeledExpression -> getFunctionLiteral(expression.getBaseExpression())
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun applyTo(element: JetCallExpression, editor: Editor) {
|
override fun applyTo(element: JetCallExpression, editor: Editor) {
|
||||||
element.moveLambdaOutsideParentheses()
|
element.moveFunctionLiteralOutsideParentheses()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -391,8 +391,9 @@ public class JetFunctionCallUsage extends JetUsageInfo<JetCallElement> {
|
|||||||
element.deleteChildRange(KotlinPackage.first(lambdaArguments), KotlinPackage.last(lambdaArguments));
|
element.deleteChildRange(KotlinPackage.first(lambdaArguments), KotlinPackage.last(lambdaArguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: this is not correct!
|
||||||
JetValueArgument lastArgument = KotlinPackage.lastOrNull(newArgumentList.getArguments());
|
JetValueArgument lastArgument = KotlinPackage.lastOrNull(newArgumentList.getArguments());
|
||||||
boolean hasTrailingLambdaInArgumentListAfter = lastArgument != null && lastArgument.getArgumentExpression() instanceof JetFunctionLiteralExpression;
|
boolean hasTrailingLambdaInArgumentListAfter = lastArgument != null && PsiPackage.unpackFunctionLiteral(lastArgument.getArgumentExpression()) != null;
|
||||||
|
|
||||||
arguments = (JetValueArgumentList) arguments.replace(newArgumentList);
|
arguments = (JetValueArgumentList) arguments.replace(newArgumentList);
|
||||||
|
|
||||||
@@ -430,11 +431,11 @@ public class JetFunctionCallUsage extends JetUsageInfo<JetCallElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasTrailingLambdaInArgumentListAfter) {
|
if (hasTrailingLambdaInArgumentListAfter) {
|
||||||
JetCallElement newCallElement =
|
JetCallExpression newCallExpression =
|
||||||
(JetCallElement) (newElement instanceof JetQualifiedExpression
|
(JetCallExpression) (newElement instanceof JetQualifiedExpression
|
||||||
? ((JetQualifiedExpression) newElement).getSelectorExpression()
|
? ((JetQualifiedExpression) newElement).getSelectorExpression()
|
||||||
: newElement);
|
: newElement);
|
||||||
PsiModificationUtilPackage.moveLambdaOutsideParentheses(newCallElement);
|
PsiModificationUtilPackage.moveFunctionLiteralOutsideParentheses(newCallExpression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith(
|
|||||||
psiFactory.createArgument(replacement)
|
psiFactory.createArgument(replacement)
|
||||||
}
|
}
|
||||||
|
|
||||||
val functionLiteralArgument = newCallExpression.getFunctionLiteralArguments().head!!
|
val functionLiteralArgument = newCallExpression.getFunctionLiteralArguments().firstOrNull()!!
|
||||||
val valueArgumentList = newCallExpression.getValueArgumentList() ?: psiFactory.createCallArguments("()")
|
val valueArgumentList = newCallExpression.getValueArgumentList() ?: psiFactory.createCallArguments("()")
|
||||||
|
|
||||||
valueArgumentList.addArgument(argument)
|
valueArgumentList.addArgument(argument)
|
||||||
@@ -69,20 +69,20 @@ fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith(
|
|||||||
return oldCallExpression.replace(newCallExpression) as JetCallExpression
|
return oldCallExpression.replace(newCallExpression) as JetCallExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
fun JetCallElement.moveLambdaOutsideParentheses() {
|
fun JetCallExpression.moveFunctionLiteralOutsideParentheses() {
|
||||||
val args = getValueArgumentsInParentheses()
|
assert(getFunctionLiteralArguments().isEmpty())
|
||||||
val functionLiteral = args.last!!.getArgumentExpression()?.getText()
|
val argumentList = getValueArgumentList()!!
|
||||||
val calleeText = getCalleeExpression()?.getText()
|
val argument = argumentList.getArguments().last()
|
||||||
if (calleeText == null || functionLiteral == null) return
|
val expression = argument.getArgumentExpression()!!
|
||||||
|
assert(expression.unpackFunctionLiteral() != null)
|
||||||
|
|
||||||
val params = args.subList(0, args.size - 1).map { it.asElement().getText() ?: "" }.joinToString(", ", "(", ")")
|
val dummyCall = JetPsiFactory(this).createExpressionByPattern("foo()$0={}$", expression) as JetCallExpression
|
||||||
|
val functionLiteralArgument = dummyCall.getFunctionLiteralArguments().single()
|
||||||
val newCall =
|
this.add(functionLiteralArgument)
|
||||||
if (params == "()") {
|
if (argumentList.getArguments().size() > 1) {
|
||||||
"$calleeText $functionLiteral"
|
argumentList.removeArgument(argument)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"$calleeText$params $functionLiteral"
|
argumentList.delete()
|
||||||
}
|
}
|
||||||
replace(JetPsiFactory(this).createExpression(newCall))
|
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// ERROR: Unresolved reference: it
|
// ERROR: Unresolved reference: it
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
bar <caret>{ it }
|
bar<caret> { it }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bar(a: Int = 0, f: (Int) -> Int) { }
|
fun bar(a: Int = 0, f: (Int) -> Int) { }
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
fun foo(p: (Int, () -> Int) -> Unit) {
|
fun foo(p: (Int, () -> Int) -> Unit) {
|
||||||
p(1) <caret>{ 2 }
|
p(1<caret>) { 2 }
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo() {
|
||||||
|
bar(<caret>{ it }) {it}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(p1: (Int) -> Int, p2: (Int) -> Int) { }
|
||||||
@@ -3,7 +3,7 @@ fun bar(a: Int, b: Int, f: () -> Unit) {}
|
|||||||
fun foo(a: Int, b: Int) = 2
|
fun foo(a: Int, b: Int) = 2
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
bar(1, 2) {
|
bar(1, 2 /* , , */) {
|
||||||
val a = foo(1, 2)
|
val a = foo(1, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4951,6 +4951,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inapplicableAlreadyHasFunctionLiteral.kt")
|
||||||
|
public void testInapplicableAlreadyHasFunctionLiteral() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableAlreadyHasFunctionLiteral.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inapplicableOptionalParametersAfter.kt")
|
@TestMetadata("inapplicableOptionalParametersAfter.kt")
|
||||||
public void testInapplicableOptionalParametersAfter() throws Exception {
|
public void testInapplicableOptionalParametersAfter() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableOptionalParametersAfter.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableOptionalParametersAfter.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user