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
|
ALSO, APPLY -> property
|
||||||
else -> first
|
else -> first
|
||||||
} ?: return
|
} ?: return
|
||||||
val parent = element.parent
|
val parent = element.parent.let { if (it is KtBinaryExpression) it.parent else it }
|
||||||
|
|
||||||
val psiFactory = KtPsiFactory(element)
|
val psiFactory = KtPsiFactory(element)
|
||||||
val (scopeFunctionCall, block) = psiFactory.createScopeFunctionCall(propertyOrFirst) ?: return
|
val (scopeFunctionCall, block) = psiFactory.createScopeFunctionCall(propertyOrFirst) ?: return
|
||||||
block.addRange(property?.nextSibling ?: first, last)
|
block.addRange(property?.nextSibling ?: first, last)
|
||||||
block.children.forEach { child ->
|
block.children.forEach { replace(it, referenceName, psiFactory) }
|
||||||
when (child) {
|
parent.addBefore(scopeFunctionCall, propertyOrFirst)
|
||||||
is KtDotQualifiedExpression -> {
|
parent.deleteChildRange(propertyOrFirst, last)
|
||||||
val replaced = child.deleteFirstReceiver()
|
}
|
||||||
if (scopeFunction.isParameterScope) {
|
|
||||||
replaced.replace(psiFactory.createExpressionByPattern("${scopeFunction.receiver}.$0", replaced))
|
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 ->
|
is KtCallExpression -> {
|
||||||
if (arg.getArgumentExpression()?.text == referenceName) {
|
element.valueArguments.forEach { arg ->
|
||||||
arg.replace(psiFactory.createArgument(scopeFunction.receiver))
|
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>? {
|
private fun KtExpression.collectTargetElements(): Pair<List<PsiElement>, String>? {
|
||||||
@@ -100,7 +107,8 @@ sealed class ConvertToScopeIntention(
|
|||||||
val property = prevProperty() ?: return null
|
val property = prevProperty() ?: return null
|
||||||
val referenceName = property.name ?: return null
|
val referenceName = property.name ?: return null
|
||||||
val targets = property.collectTargetElements(referenceName, forward = true).toList()
|
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
|
targets to referenceName
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@@ -116,7 +124,8 @@ sealed class ConvertToScopeIntention(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun KtExpression.collectTargetElements(referenceName: String, forward: Boolean): Sequence<PsiElement> {
|
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 }
|
.filter { it !is PsiWhiteSpace && it !is PsiComment }
|
||||||
.takeWhile { it.isTarget(referenceName) }
|
.takeWhile { it.isTarget(referenceName) }
|
||||||
}
|
}
|
||||||
@@ -135,6 +144,15 @@ sealed class ConvertToScopeIntention(
|
|||||||
if (valueArguments.none { it.getArgumentExpression()?.text == referenceName }) return false
|
if (valueArguments.none { it.getArgumentExpression()?.text == referenceName }) return false
|
||||||
if (lambdaArguments.isNotEmpty() || valueArguments.any { it.text == scopeFunction.receiver }) 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 ->
|
else ->
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -142,7 +160,8 @@ sealed class ConvertToScopeIntention(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun KtExpression.prevProperty(): KtProperty? {
|
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>? {
|
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);
|
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")
|
@TestMetadata("callExpression.kt")
|
||||||
public void testCallExpression() throws Exception {
|
public void testCallExpression() throws Exception {
|
||||||
runTest("idea/testData/intentions/convertToScope/convertToApply/callExpression.kt");
|
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);
|
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")
|
@TestMetadata("callExpression.kt")
|
||||||
public void testCallExpression() throws Exception {
|
public void testCallExpression() throws Exception {
|
||||||
runTest("idea/testData/intentions/convertToScope/convertToRun/callExpression.kt");
|
runTest("idea/testData/intentions/convertToScope/convertToRun/callExpression.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user