[kotlin] K2 Introduce Variable: use psi matcher to enable extraction of multiple occurrences

^KTIJ-27679 Fixed
^KTIJ-27681 Fixed
^KTIJ-27861 Fixed

GitOrigin-RevId: 5a33e3dba0f7f7d94997fdeaf6294db26ec20154
This commit is contained in:
aleksandrina-streltsova
2024-02-18 22:13:56 +00:00
committed by intellij-monorepo-bot
parent 3caea821fd
commit e80a56e53b
69 changed files with 31 additions and 112 deletions
@@ -104,7 +104,7 @@ object K1IntroduceVariableHandler : KotlinIntroduceVariableHandler() {
ConvertToBlockBodyIntention.Holder.convert(declaration)
}
fun KtExpression.findOccurrences(occurrenceContainer: PsiElement): List<KtExpression> =
override fun KtExpression.findOccurrences(occurrenceContainer: KtElement): List<KtExpression> =
toRange().match(occurrenceContainer, KotlinPsiUnifier.DEFAULT).mapNotNull {
val candidate = it.range.elements.first()
@@ -58,7 +58,7 @@ internal class KotlinIntroduceVariableServiceK1Impl(private val project: Project
override fun findOccurrences(
expression: KtExpression,
occurrenceContainer: PsiElement
occurrenceContainer: KtElement
): List<KtExpression> = with(K1IntroduceVariableHandler) {
expression.findOccurrences(occurrenceContainer)
}
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
1
1
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
val i = 1
i
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo() {
val z = <selection>123</selection>
val x: (Int) -> Int = { 123}
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo() {
val i = 123
val z = i
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo(a: Int) =
if (a > 1) {
(<selection>a + 1</selection>) * (a - 1)
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo(a: Int): Int {
val i = a + 1
return if (a > 1) {
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
if (true) {
if (true) {
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
val i = 1
if (true) {
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
if (true) {
<selection>1</selection>
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
val i = 1
if (true) {
@@ -1,4 +1,3 @@
// IGNORE_K2
fun test() {
<selection>1</selection>
1
@@ -1,4 +1,3 @@
// IGNORE_K2
fun test() {
val i = 1<caret>
i
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a(x: Int) {
val a = <selection>x + 1</selection>
"it's a number ${x + 1}"
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a(x: Int) {
val i = x + 1
val a = i
@@ -1,4 +1,3 @@
// IGNORE_K2
fun main(args: Array<String>) {
val myA = A()
println(<selection>myA.prop</selection>)
@@ -1,4 +1,3 @@
// IGNORE_K2
fun main(args: Array<String>) {
val myA = A()
val prop = myA.prop
@@ -1,4 +1,3 @@
// IGNORE_K2
val a = 2
val x: Int get() =
@@ -1,4 +1,3 @@
// IGNORE_K2
val a = 2
val x: Int get() {
@@ -1,4 +1,3 @@
// IGNORE_K2
class Bar {
}
@@ -1,4 +1,3 @@
// IGNORE_K2
class Bar {
}
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
val x = <selection>Foo()</selection>
println(Foo())
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
val foo = Foo()
val x = foo
@@ -1,4 +1,3 @@
// IGNORE_K2
class A {
fun test() = 1
}
@@ -1,4 +1,3 @@
// IGNORE_K2
class A {
fun test() = 1
}
@@ -1,4 +1,3 @@
// IGNORE_K2
class A {
fun test() = 1
}
@@ -1,4 +1,3 @@
// IGNORE_K2
class A {
fun test() = 1
}
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
when (1) {
is 3 -> 1
@@ -1,4 +1,3 @@
// IGNORE_K2
fun a() {
val i = 1
when (i) {
@@ -1,4 +1,3 @@
// IGNORE_K2
// WITH_STDLIB
class Foo(var bar: IntArray)
@@ -1,4 +1,3 @@
// IGNORE_K2
// WITH_STDLIB
class Foo(var bar: IntArray)
@@ -1,4 +1,3 @@
// IGNORE_K2
interface T {
fun foo(): Int
}
@@ -1,4 +1,3 @@
// IGNORE_K2
interface T {
fun foo(): Int
}
@@ -1,5 +0,0 @@
// WITH_STDLIB
fun f() {
val listOf = listOf<Int>()
val v : List<Int> = (listOf)
}
@@ -1,6 +0,0 @@
fun foo(f: () -> Int) { }
fun test() {
val i = 1 + 2
foo(fun() = (i) * 3)
}
@@ -1,6 +0,0 @@
fun foo(f: () -> Int) { }
fun test() {
val i = 1 + 2
foo(fun(): Int { return (i) * 3 })
}
@@ -1,6 +0,0 @@
fun foo(f: () -> Int) { }
fun test() {
val i = 1 + 2
foo { (i) * 3 }
}
@@ -1,6 +0,0 @@
fun foo(f: () -> Int) = f()
fun test() {
val i = 1 + 2
foo(fun() = foo(fun() = (i) * 3))
}
@@ -1,6 +0,0 @@
fun foo(f: () -> Int) = f()
fun test() {
val i = 1 + 2
foo { foo(fun() = (i) * 3) }
}
@@ -1,6 +0,0 @@
fun foo(f: () -> Int) = f()
fun test() {
val i = 1 + 2
foo { foo { (i) * 3 } }
}
@@ -1,6 +0,0 @@
fun foo(f: () -> Int) = f()
fun test() {
val i = 1 + 2
foo(fun() = foo { (i) * 3 })
}
@@ -1,4 +1,4 @@
// IGNORE_K2
// INPLACE_VARIABLE_NAME: test1
fun test() = "123"
open class First(val s: String)
@@ -1,4 +1,4 @@
// IGNORE_K2
// INPLACE_VARIABLE_NAME: test1
fun test() = "123"
open class First(val s: String)
@@ -1,4 +1,3 @@
// IGNORE_K2
fun test() = "123"
val f = <selection>test()</selection> + "456"
@@ -1,4 +1,3 @@
// IGNORE_K2
fun test() = "123"
val test = test()
@@ -1,4 +1,4 @@
// IGNORE_K2
// INPLACE_VARIABLE_NAME: test1
fun test() = "123"
val f = <selection>test()</selection> + "456"
@@ -1,4 +1,4 @@
// IGNORE_K2
// INPLACE_VARIABLE_NAME: test1
fun test() = "123"
val test1 = test()
@@ -1,5 +0,0 @@
// WITH_STDLIB
fun test() {
val pair = 1 to 2
(pair).first
}
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo(): Int {
val s: String? = ""
return if (true) {
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo(): Int {
val s: String? = ""
val length = s!!.length
@@ -1,3 +1,2 @@
// IGNORE_K2
1 + 2
<selection>2</selection> + 3
@@ -1,4 +1,3 @@
// IGNORE_K2
val i = 2
1 + i
i + 3
@@ -1,4 +1,3 @@
// IGNORE_K2
// WITH_STDLIB
class Foo(var bar: Bar)
class Bar(var baz: Int)
@@ -1,4 +1,3 @@
// IGNORE_K2
// WITH_STDLIB
class Foo(var bar: Bar)
class Bar(var baz: Int)
@@ -1,4 +1,3 @@
// IGNORE_K2
// WITH_STDLIB
class Foo(var bar: Int)
@@ -1,4 +1,3 @@
// IGNORE_K2
// WITH_STDLIB
class Foo(var bar: Int)
@@ -1,4 +1,4 @@
// IGNORE_K2
// INPLACE_VARIABLE_NAME: s
fun foo(a: Int): String {
val x = "abc$a"
val y = "abc${a}"
@@ -1,4 +1,4 @@
// IGNORE_K2
// INPLACE_VARIABLE_NAME: s
fun foo(a: Int): String {
val s = "abc$a"
val x = s
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo(a: Int): String {
val x = "-${a + 1}"
val y = "x${a + 1}y"
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo(a: Int): String {
val i = a + 1
val x = "-$i"
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo(a: Int): String {
val x = "-$a"
val y = "x${a}y"
@@ -1,4 +1,3 @@
// IGNORE_K2
fun foo(a: Int): String {
val a1 = a
val x = "-$a1"
@@ -1,4 +1,3 @@
// IGNORE_K2
fun (String.() -> String).foo(x : String) {
x.<selection>this</selection>()
x.this@foo()
@@ -1,4 +1,3 @@
// IGNORE_K2
fun (String.() -> String).foo(x : String) {
val function = this
x.function()
@@ -62,6 +62,8 @@ abstract class KotlinIntroduceVariableHandler : RefactoringActionHandler {
onNonInteractiveFinish: ((KtDeclaration) -> Unit)? = null,
)
abstract fun KtExpression.findOccurrences(occurrenceContainer: KtElement): List<KtExpression>
/**
* @param contained expression, which is:
* * parent of extracted expression before refactoring
@@ -20,7 +20,7 @@ interface KotlinIntroduceVariableService {
): PsiElement?
fun getContainersForExpression(expression: KtExpression): List<KotlinIntroduceVariableHelper.Containers>
fun findOccurrences(expression: KtExpression, occurrenceContainer: PsiElement): List<KtExpression>
fun findOccurrences(expression: KtExpression, occurrenceContainer: KtElement): List<KtExpression>
fun doRefactoringWithContainer(
editor: Editor?,
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.idea.codeinsight.utils.ConvertToBlockBodyUtils
import org.jetbrains.kotlin.idea.codeinsight.utils.NamedArgumentUtils
import org.jetbrains.kotlin.idea.codeinsight.utils.addTypeArguments
import org.jetbrains.kotlin.idea.codeinsight.utils.getRenderedTypeArguments
import org.jetbrains.kotlin.idea.k2.refactoring.introduce.K2SemanticMatcher
import org.jetbrains.kotlin.idea.refactoring.KotlinCommonRefactoringSettings
import org.jetbrains.kotlin.idea.refactoring.introduce.KotlinIntroduceVariableContext
import org.jetbrains.kotlin.idea.refactoring.introduce.KotlinIntroduceVariableHandler
@@ -208,7 +209,8 @@ object K2IntroduceVariableHandler : KotlinIntroduceVariableHandler() {
val isInplaceAvailable = editor != null
val allOccurrences = listOf(expression) // TODO: KTIJ-27861
val allOccurrences = occurrencesToReplace ?: expression.findOccurrences(containers.occurrenceContainer)
val callback = Pass.create { replaceChoice: OccurrencesChooser.ReplaceChoice ->
val allReplaces = when (replaceChoice) {
OccurrencesChooser.ReplaceChoice.ALL -> allOccurrences
@@ -345,6 +347,19 @@ object K2IntroduceVariableHandler : KotlinIntroduceVariableHandler() {
}
}
override fun KtExpression.findOccurrences(occurrenceContainer: KtElement): List<KtExpression> =
analyzeInModalWindow(contextElement = this, KotlinBundle.message("find.usages.prepare.dialog.progress")) {
K2SemanticMatcher.findMatches(patternElement = this@findOccurrences, scopeElement = occurrenceContainer)
.filterNot { it.isAssignmentLHS() }
.mapNotNull { match ->
when (match) {
is KtExpression -> match
is KtStringTemplateEntryWithExpression -> match.expression
else -> errorWithAttachment("Unexpected candidate element ${match::class.java}") { withPsiEntry("match", match) }
}
}
}.ifEmpty { listOf(this) }
private fun areTypeArgumentsNeededForCorrectTypeInference(expression: KtExpression): Boolean {
val call = expression.getPossiblyQualifiedCallExpression() ?: return false
if (call.typeArgumentList != null) return false
@@ -45,8 +45,10 @@ internal class KotlinIntroduceVariableServiceK2Impl(private val project: Project
override fun findOccurrences(
expression: KtExpression,
occurrenceContainer: PsiElement
): List<KtExpression> = listOf(expression)
occurrenceContainer: KtElement,
): List<KtExpression> = with(K2IntroduceVariableHandler) {
expression.findOccurrences(occurrenceContainer)
}
override fun doRefactoringWithContainer(
editor: Editor?,