Convert to scope function: also convert binary expression
#KT-30228 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
e340af51df
commit
8728bc0820
@@ -68,30 +68,37 @@ sealed class ConvertToScopeIntention(
|
||||
ALSO, APPLY -> property
|
||||
else -> first
|
||||
} ?: return
|
||||
val parent = element.parent
|
||||
val parent = element.parent.let { if (it is KtBinaryExpression) it.parent else it }
|
||||
|
||||
val psiFactory = KtPsiFactory(element)
|
||||
val (scopeFunctionCall, block) = psiFactory.createScopeFunctionCall(propertyOrFirst) ?: return
|
||||
block.addRange(property?.nextSibling ?: first, last)
|
||||
block.children.forEach { child ->
|
||||
when (child) {
|
||||
is KtDotQualifiedExpression -> {
|
||||
val replaced = child.deleteFirstReceiver()
|
||||
if (scopeFunction.isParameterScope) {
|
||||
replaced.replace(psiFactory.createExpressionByPattern("${scopeFunction.receiver}.$0", replaced))
|
||||
}
|
||||
block.children.forEach { replace(it, referenceName, psiFactory) }
|
||||
parent.addBefore(scopeFunctionCall, propertyOrFirst)
|
||||
parent.deleteChildRange(propertyOrFirst, last)
|
||||
}
|
||||
|
||||
private fun replace(element: PsiElement, referenceName: String, psiFactory: KtPsiFactory) {
|
||||
when (element) {
|
||||
is KtDotQualifiedExpression -> {
|
||||
val replaced = element.deleteFirstReceiver()
|
||||
if (scopeFunction.isParameterScope) {
|
||||
replaced.replace(psiFactory.createExpressionByPattern("${scopeFunction.receiver}.$0", replaced))
|
||||
}
|
||||
is KtCallExpression -> {
|
||||
child.valueArguments.forEach { arg ->
|
||||
if (arg.getArgumentExpression()?.text == referenceName) {
|
||||
arg.replace(psiFactory.createArgument(scopeFunction.receiver))
|
||||
}
|
||||
}
|
||||
is KtCallExpression -> {
|
||||
element.valueArguments.forEach { arg ->
|
||||
if (arg.getArgumentExpression()?.text == referenceName) {
|
||||
arg.replace(psiFactory.createArgument(scopeFunction.receiver))
|
||||
}
|
||||
}
|
||||
}
|
||||
is KtBinaryExpression -> {
|
||||
listOfNotNull(element.left, element.right).forEach {
|
||||
replace(it, referenceName, psiFactory)
|
||||
}
|
||||
}
|
||||
}
|
||||
parent.addBefore(scopeFunctionCall, propertyOrFirst)
|
||||
parent.deleteChildRange(propertyOrFirst, last)
|
||||
}
|
||||
|
||||
private fun KtExpression.collectTargetElements(): Pair<List<PsiElement>, String>? {
|
||||
@@ -100,7 +107,8 @@ sealed class ConvertToScopeIntention(
|
||||
val property = prevProperty() ?: return null
|
||||
val referenceName = property.name ?: return null
|
||||
val targets = property.collectTargetElements(referenceName, forward = true).toList()
|
||||
if (this !is KtProperty && this !in targets) return null
|
||||
val parentOrThis = parent as? KtBinaryExpression ?: this
|
||||
if (this !is KtProperty && parentOrThis !in targets) return null
|
||||
targets to referenceName
|
||||
}
|
||||
else -> {
|
||||
@@ -116,7 +124,8 @@ sealed class ConvertToScopeIntention(
|
||||
}
|
||||
|
||||
private fun KtExpression.collectTargetElements(referenceName: String, forward: Boolean): Sequence<PsiElement> {
|
||||
return siblings(forward, withItself = false)
|
||||
val parentOrThis = parent as? KtBinaryExpression ?: this
|
||||
return parentOrThis.siblings(forward, withItself = false)
|
||||
.filter { it !is PsiWhiteSpace && it !is PsiComment }
|
||||
.takeWhile { it.isTarget(referenceName) }
|
||||
}
|
||||
@@ -135,6 +144,15 @@ sealed class ConvertToScopeIntention(
|
||||
if (valueArguments.none { it.getArgumentExpression()?.text == referenceName }) return false
|
||||
if (lambdaArguments.isNotEmpty() || valueArguments.any { it.text == scopeFunction.receiver }) return false
|
||||
}
|
||||
is KtBinaryExpression -> {
|
||||
val left = this.left ?: return false
|
||||
val right = this.right ?: return false
|
||||
if (left !is KtDotQualifiedExpression && left !is KtCallExpression
|
||||
&& right !is KtDotQualifiedExpression && right !is KtCallExpression
|
||||
) return false
|
||||
if ((left is KtDotQualifiedExpression || left is KtCallExpression) && !left.isTarget(referenceName)) return false
|
||||
if ((right is KtDotQualifiedExpression || right is KtCallExpression) && !right.isTarget(referenceName)) return false
|
||||
}
|
||||
else ->
|
||||
return false
|
||||
}
|
||||
@@ -142,7 +160,8 @@ sealed class ConvertToScopeIntention(
|
||||
}
|
||||
|
||||
private fun KtExpression.prevProperty(): KtProperty? {
|
||||
return siblings(forward = false, withItself = true).firstOrNull { it is KtProperty && it.isLocal } as? KtProperty
|
||||
val parentOrThis = parent as? KtBinaryExpression ?: this
|
||||
return parentOrThis.siblings(forward = false, withItself = true).firstOrNull { it is KtProperty && it.isLocal } as? KtProperty
|
||||
}
|
||||
|
||||
private fun KtPsiFactory.createScopeFunctionCall(element: PsiElement): Pair<KtExpression, KtBlockExpression>? {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
fun foo() {}
|
||||
fun bar(): Int = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val a = A()
|
||||
a.foo()
|
||||
<caret>a.bar() + 1
|
||||
a.foo()
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
fun foo() {}
|
||||
fun bar(): Int = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val a = A().apply {
|
||||
foo()
|
||||
bar() + 1
|
||||
foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
fun foo() {}
|
||||
}
|
||||
fun baz(a: A): Int = 1
|
||||
|
||||
fun test() {
|
||||
val a = A()
|
||||
a.foo()
|
||||
1 + <caret>baz(a)
|
||||
a.foo()
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
fun foo() {}
|
||||
}
|
||||
fun baz(a: A): Int = 1
|
||||
|
||||
fun test() {
|
||||
val a = A().apply {
|
||||
foo()
|
||||
1 + baz(this)
|
||||
foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// IS_APPLICABLE: false
|
||||
class A {
|
||||
fun foo() {}
|
||||
fun bar(lambda: () -> Unit = {}): Int = 1
|
||||
}
|
||||
fun baz(a: A): Int = 1
|
||||
|
||||
fun test() {
|
||||
val a = A()
|
||||
a.foo()
|
||||
<caret>a.bar {} + baz(a)
|
||||
a.foo()
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// IS_APPLICABLE: false
|
||||
class A {
|
||||
fun foo() {}
|
||||
fun bar(lambda: () -> Unit = {}): Int = 1
|
||||
}
|
||||
fun baz(a: A): Int = 1
|
||||
|
||||
fun test() {
|
||||
val a = A()
|
||||
a.foo()
|
||||
baz(a) + <caret>a.bar {}
|
||||
a.foo()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// IS_APPLICABLE: false
|
||||
class A {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val a = A()
|
||||
a.foo()
|
||||
<caret>1 + 1
|
||||
a.foo()
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
infix fun foo(x: Any) = A()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val a = A()
|
||||
<caret>a.foo(0)
|
||||
a.foo(0)
|
||||
a.foo(0) foo 0
|
||||
a.foo(0)
|
||||
a.foo(0)
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
infix fun foo(x: Any) = A()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val a = A()
|
||||
a.run {
|
||||
foo(0)
|
||||
foo(0)
|
||||
foo(0) foo 0
|
||||
foo(0)
|
||||
foo(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
infix fun foo(x: Any) = A()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val a = A()
|
||||
a.foo(0)
|
||||
a.foo(0)
|
||||
<caret>a.foo(0) foo 0
|
||||
a.foo(0)
|
||||
a.foo(0)
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
infix fun foo(x: Any) = A()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val a = A()
|
||||
a.run {
|
||||
foo(0)
|
||||
foo(0)
|
||||
foo(0) foo 0
|
||||
foo(0)
|
||||
foo(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
infix fun foo(x: Any) = A()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val a = A()
|
||||
a.foo(0)
|
||||
a.foo(0)
|
||||
a.foo(0) foo 0
|
||||
a.foo(0)
|
||||
<caret>a.foo(0)
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// WITH_RUNTIME
|
||||
class A {
|
||||
infix fun foo(x: Any) = A()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val a = A()
|
||||
a.run {
|
||||
foo(0)
|
||||
foo(0)
|
||||
foo(0) foo 0
|
||||
foo(0)
|
||||
foo(0)
|
||||
}
|
||||
}
|
||||
@@ -7601,6 +7601,31 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertToScope/convertToApply"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression.kt")
|
||||
public void testBinaryExpression() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToApply/binaryExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression2.kt")
|
||||
public void testBinaryExpression2() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToApply/binaryExpression2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression3.kt")
|
||||
public void testBinaryExpression3() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToApply/binaryExpression3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression4.kt")
|
||||
public void testBinaryExpression4() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToApply/binaryExpression4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression5.kt")
|
||||
public void testBinaryExpression5() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToApply/binaryExpression5.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callExpression.kt")
|
||||
public void testCallExpression() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToApply/callExpression.kt");
|
||||
@@ -7704,6 +7729,21 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertToScope/convertToRun"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression.kt")
|
||||
public void testBinaryExpression() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToRun/binaryExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression2.kt")
|
||||
public void testBinaryExpression2() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToRun/binaryExpression2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression3.kt")
|
||||
public void testBinaryExpression3() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToRun/binaryExpression3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callExpression.kt")
|
||||
public void testCallExpression() throws Exception {
|
||||
runTest("idea/testData/intentions/convertToScope/convertToRun/callExpression.kt");
|
||||
|
||||
Reference in New Issue
Block a user