Fix caret position in "Convert function to property" intention

#KT-19944 Fixed
This commit is contained in:
Dmitry Gridin
2019-02-28 12:29:01 +03:00
parent 29a63ae58c
commit 41f3316981
16 changed files with 28 additions and 24 deletions
@@ -45,10 +45,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder.Target.READ_ONLY_PROPERTY
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.getStartOffsetIn
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.utils.findVariable
@@ -64,6 +61,7 @@ class ConvertFunctionToPropertyIntention :
private inner class Converter(
project: Project,
private val file: KtFile,
private val editor: Editor?,
descriptor: FunctionDescriptor
) : CallableRefactoring<FunctionDescriptor>(project, descriptor, text) {
@@ -74,7 +72,7 @@ class ConvertFunctionToPropertyIntention :
(SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(name) ?: name).asString()
}
private fun convertFunction(originalFunction: KtNamedFunction, psiFactory: KtPsiFactory) {
private fun convertFunction(originalFunction: KtNamedFunction, psiFactory: KtPsiFactory, moveCaret: Boolean) {
val propertyString = KtPsiFactory.CallableBuilder(READ_ONLY_PROPERTY).apply {
// make sure to capture all comments and line breaks
modifier(originalFunction.text.substring(0, originalFunction.funKeyword!!.getStartOffsetIn(originalFunction)))
@@ -97,14 +95,14 @@ class ConvertFunctionToPropertyIntention :
}.asString()
val replaced = originalFunction.replaced(psiFactory.createDeclaration<KtProperty>(propertyString))
editor?.caretModel?.moveToOffset(replaced.nameIdentifier!!.endOffset)
if (editor != null && moveCaret) editor.caretModel.moveToOffset(replaced.nameIdentifier!!.endOffset)
}
override fun performRefactoring(descriptorsForChange: Collection<CallableDescriptor>) {
val conflicts = MultiMap<PsiElement, String>()
val getterName = JvmAbi.getterName(callableDescriptor.name.asString())
val callables = getAffectedCallables(project, descriptorsForChange)
val mainElement = findMainElement(callables)
val kotlinCalls = ArrayList<KtCallElement>()
val kotlinRefsToRename = ArrayList<PsiReference>()
val foreignRefs = ArrayList<PsiReference>()
@@ -177,7 +175,7 @@ class ConvertFunctionToPropertyIntention :
foreignRefs.forEach { it.handleElementRename(newGetterName) }
callables.forEach {
when (it) {
is KtNamedFunction -> convertFunction(it, psiFactory)
is KtNamedFunction -> convertFunction(it, psiFactory, moveCaret = it == mainElement)
is PsiMethod -> it.name = newGetterName
}
}
@@ -186,6 +184,12 @@ class ConvertFunctionToPropertyIntention :
}
}
}
private fun findMainElement(callables: List<PsiElement>): PsiElement? {
if (editor == null) return null
val offset = editor.caretModel.offset
return callables.find { file == it.containingFile && offset in it.textRange }
}
}
override fun startInWriteAction(): Boolean = false
@@ -208,6 +212,6 @@ class ConvertFunctionToPropertyIntention :
override fun applyTo(element: KtNamedFunction, editor: Editor?) {
val descriptor = element.unsafeResolveToDescriptor(BodyResolveMode.PARTIAL) as FunctionDescriptor
Converter(element.project, editor, descriptor).run()
Converter(element.project, element.containingKtFile, editor, descriptor).run()
}
}
@@ -3,7 +3,7 @@ interface T {
}
class A(val n: Int) {
val foo
val foo<caret>
get() = object : T {
override fun bar() {
@@ -2,7 +2,7 @@ fun test() {
class X
class A(val n: Int) {
val foo get() = X()
val foo<caret> get() = X()
}
val t = A(1).foo
@@ -1,5 +1,5 @@
class A(val n: Int) {
val foo get() = n > 1
val foo<caret> get() = n > 1
}
fun test() {
@@ -1,5 +1,5 @@
class A(val n: Int) {
val foo: Boolean
val foo<caret>: Boolean
get() {
return n > 1
}
@@ -2,7 +2,7 @@ annotation class X(val s: String)
// 1
@X("") // 2
/* 3 */ fun foo<caret>(): String {
/* 3 */ fun fo<caret>o(): String {
// 4
return ""
}
@@ -1,5 +1,5 @@
class A(val n: Int) {
val foo: Boolean
val foo<caret>: Boolean
get() = n > 1
}
@@ -1,4 +1,4 @@
package test
val foo: Boolean
val foo<caret>: Boolean
get() = true
@@ -1,4 +1,4 @@
package test
val foo: Boolean
val foo<caret>: Boolean
get() = true
@@ -1,4 +1,4 @@
package test
val isFoo: Boolean
val isFoo<caret>: Boolean
get() = true
@@ -5,7 +5,7 @@ import p.foo
class A(val n: Int)
val A.foo: Boolean
val A.foo<caret>: Boolean
get() = n > 1
fun test() {
@@ -5,7 +5,7 @@ import p.foo
class A(val n: Int)
val A.foo: Boolean
val A.foo<caret>: Boolean
get() = n > 1
fun test() {
@@ -5,7 +5,7 @@ import p.isFoo
class A(val n: Int)
val A.isFoo: Boolean
val A.isFoo<caret>: Boolean
get() = n > 1
fun test() {
@@ -10,7 +10,7 @@ open class A(val n: Int): T {
}
class B: A(1) {
override val foo: Boolean
override val foo<caret>: Boolean
get() = true
}
@@ -1,3 +1,3 @@
// WITH_RUNTIME
val String.foo: String
val String.foo<caret>: String
get() = if (isEmpty()) "" else substring(1).foo
@@ -5,7 +5,7 @@ annotation class X(val s: String)
class A(val n: Int) {
val t = 1
internal @X("1")
val <T : Number> T.foo: Boolean
val <T : Number> T.foo<caret>: Boolean
get() = toInt() - n > 1
val u = 2
}