"Simplify using destructuring declaration" is now applicable for function literals #KT-13941 Fixed
This commit is contained in:
@@ -244,15 +244,16 @@ class KtPsiFactory(private val project: Project) {
|
||||
return (createFunction("fun foo() {$text}").bodyExpression as KtBlockExpression).statements.first() as KtDestructuringDeclaration
|
||||
}
|
||||
|
||||
fun createDestructuringDeclarationInFor(text: String): KtDestructuringDeclaration {
|
||||
return ((createFunction("fun foo() {for ($text in foo) {} }").bodyExpression as KtBlockExpression).statements.first() as KtForExpression).destructuringParameter!!
|
||||
}
|
||||
|
||||
fun createDestructuringParameter(text: String): KtDestructuringDeclaration {
|
||||
val dummyFun = createFunction("fun foo() { for ($text in foo) {} }")
|
||||
return ((dummyFun.bodyExpression as KtBlockExpression).statements.first() as KtForExpression).destructuringParameter!!
|
||||
}
|
||||
|
||||
fun createDestructuringParameterForLambda(text: String): KtParameter {
|
||||
val dummyFun = createFunction("fun foo() = { $text -> }")
|
||||
return (dummyFun.bodyExpression as KtLambdaExpression).functionLiteral.valueParameters.first()
|
||||
}
|
||||
|
||||
fun <TDeclaration : KtDeclaration> createDeclaration(text: String): TDeclaration {
|
||||
val file = createFile(text)
|
||||
val declarations = file.declarations
|
||||
|
||||
@@ -46,6 +46,8 @@ class DestructureIntention : SelfTargetingRangeIntention<KtParameter>(
|
||||
) {
|
||||
override fun applyTo(element: KtParameter, editor: Editor?) {
|
||||
val forLoop = element.parent as? KtForExpression
|
||||
val functionLiteral = element.parent?.parent as? KtFunctionLiteral
|
||||
if (forLoop == null && functionLiteral == null) return
|
||||
val (usagesToRemove, removeSelectorInLoopRange) = collectUsagesToRemove(element, forLoop) ?: return
|
||||
|
||||
val loopRange = forLoop?.loopRange
|
||||
@@ -62,16 +64,23 @@ class DestructureIntention : SelfTargetingRangeIntention<KtParameter>(
|
||||
}
|
||||
names.add(name)
|
||||
}
|
||||
element.replace(factory.createDestructuringDeclarationInFor("(${names.joinToString()})"))
|
||||
if (forLoop != null) {
|
||||
element.replace(factory.createDestructuringParameter("(${names.joinToString()})"))
|
||||
|
||||
if (removeSelectorInLoopRange && loopRange is KtDotQualifiedExpression) {
|
||||
loopRange.replace(loopRange.receiverExpression)
|
||||
if (removeSelectorInLoopRange && loopRange is KtDotQualifiedExpression) {
|
||||
loopRange.replace(loopRange.receiverExpression)
|
||||
}
|
||||
}
|
||||
else if (functionLiteral != null) {
|
||||
element.replace(factory.createDestructuringParameterForLambda("(${names.joinToString()})"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun applicabilityRange(element: KtParameter): TextRange? {
|
||||
val forLoop = element.parent as? KtForExpression ?: return null
|
||||
val usagesToRemove = collectUsagesToRemove(element, forLoop)
|
||||
val forLoopIfAny = element.parent as? KtForExpression
|
||||
if (forLoopIfAny == null && element.parent?.parent !is KtFunctionLiteral) return null
|
||||
|
||||
val usagesToRemove = collectUsagesToRemove(element, forLoopIfAny)
|
||||
if (usagesToRemove != null && usagesToRemove.first.isNotEmpty()) {
|
||||
return element.textRange
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.DestructureIntention
|
||||
@@ -0,0 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
data class XY(val x: Int, val y: Int)
|
||||
fun test(xys: Array<XY>) {
|
||||
xys.forEach { xy<caret> ->
|
||||
val x = xy.x
|
||||
println(x)
|
||||
val y = xy.y + x
|
||||
println(y)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
data class XY(val x: Int, val y: Int)
|
||||
fun test(xys: Array<XY>) {
|
||||
xys.forEach { (x, y) ->
|
||||
println(x)
|
||||
val y = y + x
|
||||
println(y)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
|
||||
list.forEach { klass<caret> ->
|
||||
val a = klass.a
|
||||
val b = klass.b
|
||||
}
|
||||
}
|
||||
|
||||
data class MyClass(val a: Int, val b: Int, val c: Int)
|
||||
@@ -0,0 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
|
||||
list.forEach { (a, b) ->
|
||||
}
|
||||
}
|
||||
|
||||
data class MyClass(val a: Int, val b: Int, val c: Int)
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
data class XY(val x: String, val y: String)
|
||||
|
||||
fun foo(list: List<XY>) = list.fold("") { prev, <caret>xy -> prev + xy.x + xy.y }
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
data class XY(val x: String, val y: String)
|
||||
|
||||
fun foo(list: List<XY>) = list.fold("") { prev, (x, y) -> prev + x + y }
|
||||
@@ -0,0 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
data class My(val first: String, val second: Int)
|
||||
|
||||
fun foo(list: List<My>) {
|
||||
list.forEach { my<caret> ->
|
||||
println(my.second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
data class My(val first: String, val second: Int)
|
||||
|
||||
fun foo(list: List<My>) {
|
||||
list.forEach { (first, second) ->
|
||||
println(second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
data class XY(val x: String, val y: String)
|
||||
|
||||
fun foo(list: List<XY>) = list.map { <caret>it -> it.x + it.y }
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
data class XY(val x: String, val y: String)
|
||||
|
||||
fun foo(list: List<XY>) = list.map { (x, y) -> x + y }
|
||||
@@ -0,0 +1,11 @@
|
||||
// IS_APPLICABLE: false
|
||||
// WITH_RUNTIME
|
||||
|
||||
data class XY(val x: String, val y: String)
|
||||
fun test(xys: Array<XY?>) {
|
||||
xys.forEach { xy<caret> ->
|
||||
val x = xy?.x
|
||||
val y = xy?.y
|
||||
println(x + y)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
data class XY(val x: String, val y: String)
|
||||
|
||||
fun convert(xy: XY, foo: (XY) -> String) = foo(xy)
|
||||
|
||||
fun foo(xy: XY) = convert(xy) { <caret>it -> it.x + it.y }
|
||||
@@ -0,0 +1,5 @@
|
||||
data class XY(val x: String, val y: String)
|
||||
|
||||
fun convert(xy: XY, foo: (XY) -> String) = foo(xy)
|
||||
|
||||
fun foo(xy: XY) = convert(xy) { (x, y) -> x + y }
|
||||
@@ -0,0 +1,9 @@
|
||||
data class XY(val x: String, val y: String)
|
||||
|
||||
fun convert(xy: XY, foo: (XY) -> String) = foo(xy)
|
||||
|
||||
fun foo(xy: XY) = convert(xy) { <caret>it ->
|
||||
val x = it.x
|
||||
val y = it.y
|
||||
x + y
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
data class XY(val x: String, val y: String)
|
||||
|
||||
fun convert(xy: XY, foo: (XY) -> String) = foo(xy)
|
||||
|
||||
fun foo(xy: XY) = convert(xy) { (x, y) ->
|
||||
x + y
|
||||
}
|
||||
@@ -6244,6 +6244,63 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/destructuringInLambda")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class DestructuringInLambda extends AbstractIntentionTest {
|
||||
public void testAllFilesPresentInDestructuringInLambda() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/destructuringInLambda"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("dependentLocal.kt")
|
||||
public void testDependentLocal() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/dependentLocal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("firstProperties.kt")
|
||||
public void testFirstProperties() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/firstProperties.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fold.kt")
|
||||
public void testFold() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/fold.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("last.kt")
|
||||
public void testLast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/last.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("list.kt")
|
||||
public void testList() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/list.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullable.kt")
|
||||
public void testNullable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/nullable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("variables.kt")
|
||||
public void testVariables() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/variables.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/ifNullToElvis")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user