Rename: Support inplace rename of class via primary constructor
#KT-20146 Fixed #KT-21371 Fixed
This commit is contained in:
@@ -76,7 +76,8 @@ class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isMemberInplaceRenameAvailable(element: PsiElement, context: PsiElement?) = element is KtNamedDeclaration
|
override fun isMemberInplaceRenameAvailable(element: PsiElement, context: PsiElement?) =
|
||||||
|
element is KtNamedDeclaration && element.nameIdentifier != null
|
||||||
|
|
||||||
override fun getChangeSignatureHandler() = KotlinChangeSignatureHandler()
|
override fun getChangeSignatureHandler() = KotlinChangeSignatureHandler()
|
||||||
|
|
||||||
|
|||||||
+26
-5
@@ -24,6 +24,7 @@ import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
|
|||||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer
|
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer
|
||||||
import org.jetbrains.kotlin.idea.core.unquote
|
import org.jetbrains.kotlin.idea.core.unquote
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
|
||||||
|
|
||||||
class KotlinMemberInplaceRenameHandler : MemberInplaceRenameHandler() {
|
class KotlinMemberInplaceRenameHandler : MemberInplaceRenameHandler() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -53,14 +54,34 @@ class KotlinMemberInplaceRenameHandler : MemberInplaceRenameHandler() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun PsiElement.substitute(): PsiElement {
|
||||||
|
if (this is KtPrimaryConstructor) return getContainingClassOrObject()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
override fun createMemberRenamer(element: PsiElement, elementToRename: PsiNameIdentifierOwner, editor: Editor): MemberInplaceRenamer {
|
override fun createMemberRenamer(element: PsiElement, elementToRename: PsiNameIdentifierOwner, editor: Editor): MemberInplaceRenamer {
|
||||||
val currentName = elementToRename.nameIdentifier?.text ?: ""
|
val currentElementToRename = elementToRename.substitute() as PsiNameIdentifierOwner
|
||||||
return RenamerImpl(elementToRename, element, editor, currentName, currentName)
|
val nameIdentifier = currentElementToRename.nameIdentifier
|
||||||
|
|
||||||
|
// Move caret if constructor range doesn't intersect with the one of the containing class
|
||||||
|
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) {
|
||||||
|
editor.caretModel.moveToOffset(nameIdentifier.textOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentName = nameIdentifier?.text ?: ""
|
||||||
|
return RenamerImpl(currentElementToRename, element, editor, currentName, currentName)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile): Boolean {
|
override fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile): Boolean {
|
||||||
return element is KtElement &&
|
val currentElement = element?.substitute()
|
||||||
!variableInplaceHandler.isAvailable(element, editor, file) &&
|
return currentElement is KtElement &&
|
||||||
super.isAvailable(element, editor, file)
|
!variableInplaceHandler.isAvailable(currentElement, editor, file) &&
|
||||||
|
super.isAvailable(currentElement, editor, file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ class KotlinRenameDispatcherHandler : RenameHandler {
|
|||||||
private val handlers: Array<out RenameHandler> get() = Extensions.getExtensions(EP_NAME)
|
private val handlers: Array<out RenameHandler> get() = Extensions.getExtensions(EP_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRenameHandler(dataContext: DataContext?): RenameHandler? {
|
internal fun getRenameHandler(dataContext: DataContext?): RenameHandler? {
|
||||||
val availableHandlers = handlers.filterTo(LinkedHashSet()) { it.isRenaming(dataContext) }
|
val availableHandlers = handlers.filterTo(LinkedHashSet()) { it.isRenaming(dataContext) }
|
||||||
availableHandlers.singleOrNull()?.let { return it }
|
availableHandlers.singleOrNull()?.let { return it }
|
||||||
availableHandlers.firstIsInstanceOrNull<KotlinMemberInplaceRenameHandler>()?.let { availableHandlers -= it }
|
availableHandlers.firstIsInstanceOrNull<KotlinMemberInplaceRenameHandler>()?.let { availableHandlers -= it }
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
open class Bar(val foo: Boolean)
|
||||||
|
class E : Bar(false)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
open class Foo/*rename*/(val foo: Boolean)
|
||||||
|
class E : Foo(false)
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"type": "AUTO_DETECT",
|
||||||
|
"mainFile": "test.kt",
|
||||||
|
"newName": "Bar"
|
||||||
|
}
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
open class Bar(val foo: Boolean)
|
||||||
|
class E : Bar(false)
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
open class Foo /*rename*/(val foo: Boolean)
|
||||||
|
class E : Foo(false)
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"type": "AUTO_DETECT",
|
||||||
|
"mainFile": "test.kt",
|
||||||
|
"newName": "Bar"
|
||||||
|
}
|
||||||
@@ -42,11 +42,13 @@ import com.intellij.psi.search.GlobalSearchScope
|
|||||||
import com.intellij.refactoring.BaseRefactoringProcessor
|
import com.intellij.refactoring.BaseRefactoringProcessor
|
||||||
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
|
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
|
||||||
import com.intellij.refactoring.rename.*
|
import com.intellij.refactoring.rename.*
|
||||||
|
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
|
||||||
import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory
|
import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory
|
||||||
import com.intellij.refactoring.util.CommonRefactoringUtil.RefactoringErrorHintException
|
import com.intellij.refactoring.util.CommonRefactoringUtil.RefactoringErrorHintException
|
||||||
import com.intellij.testFramework.LightProjectDescriptor
|
import com.intellij.testFramework.LightProjectDescriptor
|
||||||
import com.intellij.testFramework.PlatformTestUtil
|
import com.intellij.testFramework.PlatformTestUtil
|
||||||
import com.intellij.testFramework.UsefulTestCase
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
|
import com.intellij.testFramework.fixtures.CodeInsightTestUtil
|
||||||
import org.jetbrains.kotlin.asJava.finder.KtLightPackage
|
import org.jetbrains.kotlin.asJava.finder.KtLightPackage
|
||||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
@@ -349,9 +351,19 @@ abstract class AbstractRenameTest : KotlinLightCodeInsightFixtureTestCase() {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val handler = RenameHandlerRegistry.getInstance().getRenameHandler(dataContext) ?: return@doTestCommittingDocuments
|
var handler = RenameHandlerRegistry.getInstance().getRenameHandler(dataContext) ?: return@doTestCommittingDocuments
|
||||||
Assert.assertTrue(handler.isAvailableOnDataContext(dataContext))
|
Assert.assertTrue(handler.isAvailableOnDataContext(dataContext))
|
||||||
handler.invoke(project, editor, psiFile, dataContext)
|
if (handler is KotlinRenameDispatcherHandler) {
|
||||||
|
handler = handler.getRenameHandler(dataContext)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler is VariableInplaceRenameHandler) {
|
||||||
|
val elementToRename = psiFile.findElementAt(currentCaret.offset)!!.getNonStrictParentOfType<PsiNamedElement>()!!
|
||||||
|
CodeInsightTestUtil.doInlineRename(handler, newName, editor, elementToRename)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
handler.invoke(project, editor, psiFile, dataContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,18 @@ public class RenameTestGenerated extends AbstractRenameTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classByPrimaryConstructor/classByPrimaryConstructor.test")
|
||||||
|
public void testClassByPrimaryConstructor_ClassByPrimaryConstructor() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/classByPrimaryConstructor/classByPrimaryConstructor.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classByPrimaryConstructorWithSpace/classByPrimaryConstructorWithSpace.test")
|
||||||
|
public void testClassByPrimaryConstructorWithSpace_ClassByPrimaryConstructorWithSpace() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/classByPrimaryConstructorWithSpace/classByPrimaryConstructorWithSpace.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("classUsagesInTextFiles/classUsagesInTextFiles.test")
|
@TestMetadata("classUsagesInTextFiles/classUsagesInTextFiles.test")
|
||||||
public void testClassUsagesInTextFiles_ClassUsagesInTextFiles() throws Exception {
|
public void testClassUsagesInTextFiles_ClassUsagesInTextFiles() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/classUsagesInTextFiles/classUsagesInTextFiles.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/classUsagesInTextFiles/classUsagesInTextFiles.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user