Rename: Adapt VariableInplaceRenameHandler to Kotlin

#KT-16510 Fixed
This commit is contained in:
Alexey Sedunov
2018-02-22 22:49:44 +03:00
parent 7149a0855a
commit 0e3e387446
18 changed files with 154 additions and 78 deletions
+1
View File
@@ -2782,6 +2782,7 @@
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameByLabeledReferenceInLambdaArgumentHandler"/>
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameImportAliasByReferenceHandler"/>
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.KotlinMemberInplaceRenameHandler"/>
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler"/>
<versionInfoProvider implementation="org.jetbrains.kotlin.idea.facet.KotlinVersionInfoProviderByModuleDependencies"/>
@@ -13,13 +13,13 @@ import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.SmartPsiElementPointer
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.refactoring.getThisLabelName
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
import org.jetbrains.kotlin.idea.util.getReceiverTargetDescriptor
import org.jetbrains.kotlin.idea.util.getResolutionScope
@@ -359,6 +359,6 @@ private fun PsiElement.startInPlaceRename() {
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
editor.caretModel.moveToOffset(startOffset)
VariableInplaceRenameHandler().doRename(this, editor, null)
KotlinVariableInplaceRenameHandler().doRename(this, editor, null)
}
}
@@ -19,8 +19,8 @@ package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiDocumentManager
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.KtLambdaExpression
@@ -49,6 +49,6 @@ class ReplaceItWithExplicitFunctionLiteralParamIntention : SelfTargetingOffsetIn
val paramToRename = functionLiteral.valueParameters.single()
editor.caretModel.moveToOffset(element.textOffset)
VariableInplaceRenameHandler().doRename(paramToRename, editor, null)
KotlinVariableInplaceRenameHandler().doRename(paramToRename, editor, null)
}
}
@@ -23,8 +23,8 @@ import com.intellij.psi.PsiNameIdentifierOwner
import com.intellij.refactoring.RefactoringActionHandler
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.KotlinExtractInterfaceHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.KotlinExtractSuperclassHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.KotlinIntroduceLambdaParameterHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.KotlinIntroduceParameterHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
@@ -32,7 +32,8 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinI
import org.jetbrains.kotlin.idea.refactoring.pullUp.KotlinPullUpHandler
import org.jetbrains.kotlin.idea.refactoring.pushDown.KotlinPushDownHandler
import org.jetbrains.kotlin.idea.refactoring.safeDelete.canDeleteElement
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtConstructor
import org.jetbrains.kotlin.psi.KtElement
class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
override fun isSafeDeleteAvailable(element: PsiElement) = element.canDeleteElement()
@@ -51,31 +52,6 @@ class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
fun getExtractFunctionToScopeHandler(): RefactoringActionHandler =
ExtractKotlinFunctionHandler(true, ExtractKotlinFunctionHandler.InteractiveExtractionHelper)
override fun isInplaceRenameAvailable(element: PsiElement, context: PsiElement?): Boolean {
when (element) {
is KtTypeParameter -> return true
is KtProperty -> {
if (element.isLocal) return true
}
is KtDestructuringDeclarationEntry -> return true
is KtParameter -> {
val parent = element.parent
if (parent is KtForExpression) {
return true
}
if (parent is KtParameterList) {
val grandparent = parent.parent
return grandparent is KtCatchClause || grandparent is KtFunctionLiteral
}
}
is KtLabeledExpression, is KtImportAlias -> return true
}
return false
}
override fun isMemberInplaceRenameAvailable(element: PsiElement, context: PsiElement?) =
element is KtNamedDeclaration && element.nameIdentifier != null
override fun getChangeSignatureHandler() = KotlinChangeSignatureHandler()
override fun getPullUpHandler() = KotlinPullUpHandler()
@@ -18,29 +18,21 @@ package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.editor.Editor
import com.intellij.psi.*
import com.intellij.refactoring.RefactoringActionHandler
import com.intellij.refactoring.rename.inplace.MemberInplaceRenameHandler
import com.intellij.refactoring.rename.inplace.MemberInplaceRenamer
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer
import org.jetbrains.kotlin.idea.core.unquote
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
class KotlinMemberInplaceRenameHandler : MemberInplaceRenameHandler() {
companion object {
private val variableInplaceHandler = object : VariableInplaceRenameHandler() {
override public fun isAvailable(element: PsiElement?, editor: Editor?, file: PsiFile?): Boolean {
return super.isAvailable(element, editor, file)
}
}
}
private class RenamerImpl(
elementToRename: PsiNamedElement,
substitutedElement: PsiElement?,
editor: Editor,
currentName: String,
oldName: String
elementToRename: PsiNamedElement,
substitutedElement: PsiElement?,
editor: Editor,
currentName: String,
oldName: String
) : MemberInplaceRenamer(elementToRename, substitutedElement, editor, currentName, oldName) {
override fun acceptReference(reference: PsiReference): Boolean {
val refElement = reference.element
@@ -49,6 +41,10 @@ class KotlinMemberInplaceRenameHandler : MemberInplaceRenameHandler() {
return referenceText == myElementToRename.name
}
override fun startsOnTheSameElement(handler: RefactoringActionHandler?, element: PsiElement?): Boolean {
return variable == element && (handler is MemberInplaceRenameHandler || handler is KotlinRenameDispatcherHandler)
}
override fun createInplaceRenamerToRestart(variable: PsiNamedElement, editor: Editor, initialName: String): VariableInplaceRenamer {
return RenamerImpl(variable, substituted, editor, initialName, myOldName)
}
@@ -67,10 +63,10 @@ class KotlinMemberInplaceRenameHandler : MemberInplaceRenameHandler() {
val offset = editor.caretModel.offset
val editorPsiFile = PsiDocumentManager.getInstance(element.project).getPsiFile(editor.document)
if (nameIdentifier != null
&& editorPsiFile == elementToRename.containingFile
&& elementToRename is KtPrimaryConstructor
&& offset !in nameIdentifier.textRange
&& offset in elementToRename.textRange) {
&& editorPsiFile == elementToRename.containingFile
&& elementToRename is KtPrimaryConstructor
&& offset !in nameIdentifier.textRange
&& offset in elementToRename.textRange) {
editor.caretModel.moveToOffset(nameIdentifier.textOffset)
}
@@ -79,9 +75,8 @@ class KotlinMemberInplaceRenameHandler : MemberInplaceRenameHandler() {
}
override fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile): Boolean {
val currentElement = element?.substitute()
return currentElement is KtElement &&
!variableInplaceHandler.isAvailable(currentElement, editor, file) &&
super.isAvailable(currentElement, editor, file)
if (!editor.settings.isVariableInplaceRenameEnabled) return false
val currentElement = element?.substitute() as? KtNamedDeclaration ?: return false
return currentElement.nameIdentifier != null && !KotlinVariableInplaceRenameHandler.isInplaceRenameAvailable(currentElement)
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.editor.Editor
import com.intellij.psi.*
import com.intellij.refactoring.RefactoringActionHandler
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer
import org.jetbrains.kotlin.idea.core.unquote
import org.jetbrains.kotlin.psi.*
open class KotlinVariableInplaceRenameHandler : VariableInplaceRenameHandler() {
companion object {
fun isInplaceRenameAvailable(element: PsiElement): Boolean {
when (element) {
is KtTypeParameter -> return true
is KtProperty -> {
if (element.isLocal) return true
}
is KtDestructuringDeclarationEntry -> return true
is KtParameter -> {
val parent = element.parent
if (parent is KtForExpression) {
return true
}
if (parent is KtParameterList) {
val grandparent = parent.parent
return grandparent is KtCatchClause || grandparent is KtFunctionLiteral
}
}
is KtLabeledExpression, is KtImportAlias -> return true
}
return false
}
}
protected open class RenamerImpl : VariableInplaceRenamer {
constructor(elementToRename: PsiNamedElement, editor: Editor): super(elementToRename, editor)
constructor(
elementToRename: PsiNamedElement,
editor: Editor,
currentName: String,
oldName: String
) : super(elementToRename, editor, editor.project, currentName, oldName)
override fun acceptReference(reference: PsiReference): Boolean {
val refElement = reference.element
val textRange = reference.rangeInElement
val referenceText = refElement.text.substring(textRange.startOffset, textRange.endOffset).unquote()
return referenceText == myElementToRename.name
}
override fun startsOnTheSameElement(handler: RefactoringActionHandler?, element: PsiElement?): Boolean {
return variable == element && (handler is VariableInplaceRenameHandler || handler is KotlinRenameDispatcherHandler)
}
override fun createInplaceRenamerToRestart(variable: PsiNamedElement, editor: Editor, initialName: String): VariableInplaceRenamer {
return RenamerImpl(variable, editor, initialName, myOldName)
}
}
override fun createRenamer(elementToRename: PsiElement, editor: Editor): VariableInplaceRenamer? {
val currentElementToRename = elementToRename as PsiNameIdentifierOwner
val currentName = currentElementToRename.nameIdentifier?.text ?: ""
return RenamerImpl(currentElementToRename, editor, currentName, currentName)
}
override public fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile) =
editor.settings.isVariableInplaceRenameEnabled && element != null && isInplaceRenameAvailable(element)
}
@@ -21,13 +21,12 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
class RenameBackingFieldReferenceHandler: VariableInplaceRenameHandler() {
class RenameBackingFieldReferenceHandler: KotlinVariableInplaceRenameHandler() {
override fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile): Boolean {
val refExpression = file.findElementForRename<KtSimpleNameExpression>(editor.caretModel.offset) ?: return false
if (refExpression.text != "field") return false
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.psi.PsiElement
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
@@ -31,7 +30,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class RenameByLabeledReferenceInLambdaArgumentHandler :
AbstractReferenceSubstitutionRenameHandler(VariableInplaceRenameHandler()) {
AbstractReferenceSubstitutionRenameHandler(KotlinVariableInplaceRenameHandler()) {
override fun getElementToRename(dataContext: DataContext): PsiElement? {
val refExpr = getReferenceExpression(dataContext) as? KtLabelReferenceExpression ?: return null
val context = refExpr.analyze(BodyResolveMode.PARTIAL)
@@ -16,19 +16,18 @@
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import com.intellij.psi.PsiElement
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import com.intellij.openapi.project.Project
import com.intellij.openapi.actionSystem.DataContext
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
class RenameDynamicMemberHandler: VariableInplaceRenameHandler() {
class RenameDynamicMemberHandler: KotlinVariableInplaceRenameHandler() {
override fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile): Boolean {
val callee = PsiTreeUtil.findElementOfClassAtOffset(
file, editor.caretModel.offset, KtSimpleNameExpression::class.java, false
@@ -18,11 +18,9 @@ package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.psi.PsiElement
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
class RenameImportAliasByReferenceHandler : AbstractReferenceSubstitutionRenameHandler(VariableInplaceRenameHandler()) {
class RenameImportAliasByReferenceHandler : AbstractReferenceSubstitutionRenameHandler(KotlinVariableInplaceRenameHandler()) {
override fun getElementToRename(dataContext: DataContext): PsiElement? {
val refExpr = getReferenceExpression(dataContext) ?: return null
return refExpr.mainReference.getImportAlias()
@@ -21,13 +21,12 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import org.jetbrains.kotlin.idea.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention
import org.jetbrains.kotlin.idea.intentions.isAutoCreatedItUsage
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
class RenameKotlinImplicitLambdaParameter : VariableInplaceRenameHandler() {
class RenameKotlinImplicitLambdaParameter : KotlinVariableInplaceRenameHandler() {
override fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile): Boolean {
val nameExpression = file.findElementForRename<KtNameReferenceExpression>(editor.caretModel.offset)
@@ -19,17 +19,15 @@ package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer
class VariableInplaceRenameHandlerWithFinishHook(private val onFinish: () -> Unit) : VariableInplaceRenameHandler() {
override fun createRenamer(elementToRename: PsiElement, editor: Editor?): VariableInplaceRenamer? {
return object : VariableInplaceRenamer(elementToRename as PsiNamedElement, editor) {
class VariableInplaceRenameHandlerWithFinishHook(private val onFinish: () -> Unit) : KotlinVariableInplaceRenameHandler() {
override fun createRenamer(elementToRename: PsiElement, editor: Editor): VariableInplaceRenamer? {
return object : RenamerImpl(elementToRename as PsiNamedElement, editor) {
override fun performRefactoring(): Boolean {
try {
return super.performRefactoring()
}
finally {
} finally {
onFinish()
}
}
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.idea.core.dropDefaultValue
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle
import org.jetbrains.kotlin.idea.references.AbstractKtReference
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.kotlin.resolve.source.getPsi
fun checkConflictsAndReplaceUsageInfos(
@@ -0,0 +1,7 @@
class A(val a: B)
class B(val b: Int)
fun x(`is`: A) {
val <caret>`in` = `is`.a
`in`.b
}
@@ -0,0 +1,7 @@
class A(val a: B)
class B(val b: Int)
fun x(`is`: A) {
val x = `is`.a
x.b
}
@@ -0,0 +1,7 @@
class A(val a: B)
class B(val b: Int)
fun x(<caret>`is`: A) {
val `in` = `is`.a
`in`.b
}
@@ -0,0 +1,7 @@
class A(val a: B)
class B(val b: Int)
fun x(x: A) {
val `in` = x.a
`in`.b
}
@@ -11,10 +11,11 @@ import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.refactoring.rename.inplace.MemberInplaceRenameHandler
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
import com.intellij.testFramework.fixtures.CodeInsightTestUtil
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinMemberInplaceRenameHandler
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler
import org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinImplicitLambdaParameter
import org.jetbrains.kotlin.idea.refactoring.rename.findElementForRename
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
@@ -142,6 +143,14 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
doTestMemberInplaceRename("example")
}
fun testQuotedLocalVar() {
doTestInplaceRename("x")
}
fun testQuotedParameter() {
doTestMemberInplaceRename("x")
}
private fun doTestImplicitLambdaParameter(newName: String) {
configureByFile(getTestName(false) + ".kt")
@@ -193,10 +202,10 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
}
private fun doTestMemberInplaceRename(newName: String?) {
doTestInplaceRename(newName, MemberInplaceRenameHandler())
doTestInplaceRename(newName, KotlinMemberInplaceRenameHandler())
}
private fun doTestInplaceRename(newName: String?, handler: VariableInplaceRenameHandler = VariableInplaceRenameHandler()) {
private fun doTestInplaceRename(newName: String?, handler: VariableInplaceRenameHandler = KotlinVariableInplaceRenameHandler()) {
configureByFile(getTestName(false) + ".kt")
val element = TargetElementUtil.findTargetElement(
LightPlatformCodeInsightTestCase.myEditor,