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
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
|
||||
+26
-5
@@ -24,6 +24,7 @@ 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.KtPrimaryConstructor
|
||||
|
||||
class KotlinMemberInplaceRenameHandler : MemberInplaceRenameHandler() {
|
||||
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 {
|
||||
val currentName = elementToRename.nameIdentifier?.text ?: ""
|
||||
return RenamerImpl(elementToRename, element, editor, currentName, currentName)
|
||||
val currentElementToRename = elementToRename.substitute() as PsiNameIdentifierOwner
|
||||
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 {
|
||||
return element is KtElement &&
|
||||
!variableInplaceHandler.isAvailable(element, editor, file) &&
|
||||
super.isAvailable(element, editor, file)
|
||||
val currentElement = element?.substitute()
|
||||
return currentElement is KtElement &&
|
||||
!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 fun getRenameHandler(dataContext: DataContext?): RenameHandler? {
|
||||
internal fun getRenameHandler(dataContext: DataContext?): RenameHandler? {
|
||||
val availableHandlers = handlers.filterTo(LinkedHashSet()) { it.isRenaming(dataContext) }
|
||||
availableHandlers.singleOrNull()?.let { return 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.ConflictsInTestsException
|
||||
import com.intellij.refactoring.rename.*
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
|
||||
import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil.RefactoringErrorHintException
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.PlatformTestUtil
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import com.intellij.testFramework.fixtures.CodeInsightTestUtil
|
||||
import org.jetbrains.kotlin.asJava.finder.KtLightPackage
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
@@ -349,9 +351,19 @@ abstract class AbstractRenameTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
val handler = RenameHandlerRegistry.getInstance().getRenameHandler(dataContext) ?: return@doTestCommittingDocuments
|
||||
var handler = RenameHandlerRegistry.getInstance().getRenameHandler(dataContext) ?: return@doTestCommittingDocuments
|
||||
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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testClassUsagesInTextFiles_ClassUsagesInTextFiles() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/classUsagesInTextFiles/classUsagesInTextFiles.test");
|
||||
|
||||
Reference in New Issue
Block a user