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.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder.Target.READ_ONLY_PROPERTY import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder.Target.READ_ONLY_PROPERTY
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.*
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.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.utils.findVariable import org.jetbrains.kotlin.resolve.scopes.utils.findVariable
@@ -64,6 +61,7 @@ class ConvertFunctionToPropertyIntention :
private inner class Converter( private inner class Converter(
project: Project, project: Project,
private val file: KtFile,
private val editor: Editor?, private val editor: Editor?,
descriptor: FunctionDescriptor descriptor: FunctionDescriptor
) : CallableRefactoring<FunctionDescriptor>(project, descriptor, text) { ) : CallableRefactoring<FunctionDescriptor>(project, descriptor, text) {
@@ -74,7 +72,7 @@ class ConvertFunctionToPropertyIntention :
(SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(name) ?: name).asString() (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 { val propertyString = KtPsiFactory.CallableBuilder(READ_ONLY_PROPERTY).apply {
// make sure to capture all comments and line breaks // make sure to capture all comments and line breaks
modifier(originalFunction.text.substring(0, originalFunction.funKeyword!!.getStartOffsetIn(originalFunction))) modifier(originalFunction.text.substring(0, originalFunction.funKeyword!!.getStartOffsetIn(originalFunction)))
@@ -97,14 +95,14 @@ class ConvertFunctionToPropertyIntention :
}.asString() }.asString()
val replaced = originalFunction.replaced(psiFactory.createDeclaration<KtProperty>(propertyString)) val replaced = originalFunction.replaced(psiFactory.createDeclaration<KtProperty>(propertyString))
if (editor != null && moveCaret) editor.caretModel.moveToOffset(replaced.nameIdentifier!!.endOffset)
editor?.caretModel?.moveToOffset(replaced.nameIdentifier!!.endOffset)
} }
override fun performRefactoring(descriptorsForChange: Collection<CallableDescriptor>) { override fun performRefactoring(descriptorsForChange: Collection<CallableDescriptor>) {
val conflicts = MultiMap<PsiElement, String>() val conflicts = MultiMap<PsiElement, String>()
val getterName = JvmAbi.getterName(callableDescriptor.name.asString()) val getterName = JvmAbi.getterName(callableDescriptor.name.asString())
val callables = getAffectedCallables(project, descriptorsForChange) val callables = getAffectedCallables(project, descriptorsForChange)
val mainElement = findMainElement(callables)
val kotlinCalls = ArrayList<KtCallElement>() val kotlinCalls = ArrayList<KtCallElement>()
val kotlinRefsToRename = ArrayList<PsiReference>() val kotlinRefsToRename = ArrayList<PsiReference>()
val foreignRefs = ArrayList<PsiReference>() val foreignRefs = ArrayList<PsiReference>()
@@ -177,7 +175,7 @@ class ConvertFunctionToPropertyIntention :
foreignRefs.forEach { it.handleElementRename(newGetterName) } foreignRefs.forEach { it.handleElementRename(newGetterName) }
callables.forEach { callables.forEach {
when (it) { when (it) {
is KtNamedFunction -> convertFunction(it, psiFactory) is KtNamedFunction -> convertFunction(it, psiFactory, moveCaret = it == mainElement)
is PsiMethod -> it.name = newGetterName 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 override fun startInWriteAction(): Boolean = false
@@ -208,6 +212,6 @@ class ConvertFunctionToPropertyIntention :
override fun applyTo(element: KtNamedFunction, editor: Editor?) { override fun applyTo(element: KtNamedFunction, editor: Editor?) {
val descriptor = element.unsafeResolveToDescriptor(BodyResolveMode.PARTIAL) as FunctionDescriptor 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) { class A(val n: Int) {
val foo val foo<caret>
get() = object : T { get() = object : T {
override fun bar() { override fun bar() {
@@ -2,7 +2,7 @@ fun test() {
class X class X
class A(val n: Int) { class A(val n: Int) {
val foo get() = X() val foo<caret> get() = X()
} }
val t = A(1).foo val t = A(1).foo
@@ -1,5 +1,5 @@
class A(val n: Int) { class A(val n: Int) {
val foo get() = n > 1 val foo<caret> get() = n > 1
} }
fun test() { fun test() {
@@ -1,5 +1,5 @@
class A(val n: Int) { class A(val n: Int) {
val foo: Boolean val foo<caret>: Boolean
get() { get() {
return n > 1 return n > 1
} }
@@ -2,7 +2,7 @@ annotation class X(val s: String)
// 1 // 1
@X("") // 2 @X("") // 2
/* 3 */ fun foo<caret>(): String { /* 3 */ fun fo<caret>o(): String {
// 4 // 4
return "" return ""
} }
@@ -1,5 +1,5 @@
class A(val n: Int) { class A(val n: Int) {
val foo: Boolean val foo<caret>: Boolean
get() = n > 1 get() = n > 1
} }
@@ -1,4 +1,4 @@
package test package test
val foo: Boolean val foo<caret>: Boolean
get() = true get() = true
@@ -1,4 +1,4 @@
package test package test
val foo: Boolean val foo<caret>: Boolean
get() = true get() = true
@@ -1,4 +1,4 @@
package test package test
val isFoo: Boolean val isFoo<caret>: Boolean
get() = true get() = true
@@ -5,7 +5,7 @@ import p.foo
class A(val n: Int) class A(val n: Int)
val A.foo: Boolean val A.foo<caret>: Boolean
get() = n > 1 get() = n > 1
fun test() { fun test() {
@@ -5,7 +5,7 @@ import p.foo
class A(val n: Int) class A(val n: Int)
val A.foo: Boolean val A.foo<caret>: Boolean
get() = n > 1 get() = n > 1
fun test() { fun test() {
@@ -5,7 +5,7 @@ import p.isFoo
class A(val n: Int) class A(val n: Int)
val A.isFoo: Boolean val A.isFoo<caret>: Boolean
get() = n > 1 get() = n > 1
fun test() { fun test() {
@@ -10,7 +10,7 @@ open class A(val n: Int): T {
} }
class B: A(1) { class B: A(1) {
override val foo: Boolean override val foo<caret>: Boolean
get() = true get() = true
} }
@@ -1,3 +1,3 @@
// WITH_RUNTIME // WITH_RUNTIME
val String.foo: String val String.foo<caret>: String
get() = if (isEmpty()) "" else substring(1).foo get() = if (isEmpty()) "" else substring(1).foo
@@ -5,7 +5,7 @@ annotation class X(val s: String)
class A(val n: Int) { class A(val n: Int) {
val t = 1 val t = 1
internal @X("1") internal @X("1")
val <T : Number> T.foo: Boolean val <T : Number> T.foo<caret>: Boolean
get() = toInt() - n > 1 get() = toInt() - n > 1
val u = 2 val u = 2
} }