Rename: Fix inplace refactoring for declarations with backticked names

#KT-15859 Fixed
This commit is contained in:
Alexey Sedunov
2017-05-23 19:13:34 +03:00
parent 2f88b6ef9b
commit 9c11c42ec2
14 changed files with 169 additions and 8 deletions
+1
View File
@@ -516,6 +516,7 @@
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameClassByCompanionObjectShortReferenceHandler"/>
<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"/>
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.AutomaticVariableRenamerFactory"/>
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.AutomaticVariableRenamerFactoryForJavaClass"/>
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.AutomaticVariableInJavaRenamerFactory"/>
@@ -35,14 +35,20 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
abstract class AbstractReferenceSubstitutionRenameHandler(
private val delegateHandler: RenameHandler = MemberInplaceRenameHandler()
) : PsiElementRenameHandler() {
protected fun getReferenceExpression(dataContext: DataContext): KtSimpleNameExpression? {
val caret = CommonDataKeys.CARET.getData(dataContext) ?: return null
val ktFile = CommonDataKeys.PSI_FILE.getData(dataContext) as? KtFile ?: return null
var elementAtCaret = ktFile.findElementAt(caret.offset)
if (elementAtCaret is PsiWhiteSpace) {
elementAtCaret = CodeInsightUtils.getElementAtOffsetIgnoreWhitespaceAfter(ktFile, caret.offset)
companion object {
fun getReferenceExpression(file: PsiFile, offset: Int): KtSimpleNameExpression? {
var elementAtCaret = file.findElementAt(offset)
if (elementAtCaret is PsiWhiteSpace) {
elementAtCaret = CodeInsightUtils.getElementAtOffsetIgnoreWhitespaceAfter(file, offset)
}
return elementAtCaret?.getNonStrictParentOfType<KtSimpleNameExpression>()
}
fun getReferenceExpression(dataContext: DataContext): KtSimpleNameExpression? {
val caret = CommonDataKeys.CARET.getData(dataContext) ?: return null
val ktFile = CommonDataKeys.PSI_FILE.getData(dataContext) as? KtFile ?: return null
return getReferenceExpression(ktFile, caret.offset)
}
return elementAtCaret?.getNonStrictParentOfType<KtSimpleNameExpression>()
}
protected abstract fun getElementToRename(dataContext: DataContext): PsiElement?
@@ -0,0 +1,69 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.Comparing
import com.intellij.psi.*
import com.intellij.refactoring.rename.inplace.MemberInplaceRenameHandler
import com.intellij.refactoring.rename.inplace.MemberInplaceRenamer
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.unquote
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtLabelReferenceExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class KotlinMemberInplaceRenameHandler : MemberInplaceRenameHandler() {
private class RenamerImpl(
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
val textRange = reference.rangeInElement
val referenceText = refElement.text.substring(textRange.startOffset, textRange.endOffset).unquote()
return referenceText == myElementToRename.name
}
override fun createInplaceRenamerToRestart(variable: PsiNamedElement, editor: Editor, initialName: String): VariableInplaceRenamer {
return RenamerImpl(variable, substituted, editor, initialName, myOldName)
}
}
override fun createMemberRenamer(element: PsiElement, elementToRename: PsiNameIdentifierOwner, editor: Editor): MemberInplaceRenamer {
val currentName = elementToRename.nameIdentifier?.text ?: ""
return RenamerImpl(elementToRename, element, editor, currentName, currentName)
}
override fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile): Boolean {
if (element !is KtElement) return false
if (!super.isAvailable(element, editor, file)) return false
val referenceExpression = AbstractReferenceSubstitutionRenameHandler.getReferenceExpression(file, editor.caretModel.offset) ?: return true
if (referenceExpression is KtLabelReferenceExpression) return false
if (referenceExpression.mainReference.getImportAlias() != null) return false
if (referenceExpression.analyze(BodyResolveMode.PARTIAL)[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, referenceExpression] != null) return false
return true
}
}
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
@@ -112,7 +113,10 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtil
}
override fun isIdentifierPart(file: PsiFile, text: CharSequence?, offset: Int): Boolean {
val elementAtCaret = file.findElementAt(offset)
if (elementAtCaret?.node?.elementType == KtTokens.IDENTIFIER) return true
// '(' is considered identifier part if it belongs to primary constructor without 'constructor' keyword
return file.findElementAt(offset)?.getNonStrictParentOfType<KtPrimaryConstructor>()?.textOffset == offset
return elementAtCaret?.getNonStrictParentOfType<KtPrimaryConstructor>()?.textOffset == offset
}
}
@@ -0,0 +1,8 @@
fun `fun`(n: Int) {
}
fun test() {
`fun`(1)
`fun`(2)
}
@@ -0,0 +1,8 @@
fun /*rename*/foo(n: Int) {
}
fun test() {
foo(1)
foo(2)
}
@@ -0,0 +1,5 @@
{
"type": "MARKED_ELEMENT",
"mainFile": "test.kt",
"newName": "`fun`"
}
@@ -0,0 +1,8 @@
fun foo(n: Int) {
}
fun test() {
foo(1)
foo(2)
}
@@ -0,0 +1,8 @@
fun /*rename*/`fun`(n: Int) {
}
fun test() {
`fun`(1)
`fun`(2)
}
@@ -0,0 +1,5 @@
{
"type": "MARKED_ELEMENT",
"mainFile": "test.kt",
"newName": "foo"
}
@@ -0,0 +1,8 @@
fun foo(n: Int) {
}
fun test() {
foo(1)
foo(2)
}
@@ -0,0 +1,8 @@
fun `fun`/*rename*/(n: Int) {
}
fun test() {
`fun`(1)
`fun`(2)
}
@@ -0,0 +1,5 @@
{
"type": "MARKED_ELEMENT",
"mainFile": "test.kt",
"newName": "foo"
}
@@ -342,6 +342,12 @@ public class RenameTestGenerated extends AbstractRenameTest {
doTest(fileName);
}
@TestMetadata("nonQuotedToQuoted/nonQuotedToQuoted.test")
public void testNonQuotedToQuoted_NonQuotedToQuoted() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/nonQuotedToQuoted/nonQuotedToQuoted.test");
doTest(fileName);
}
@TestMetadata("noShadowingConflictForSiblingDeclarations/noShadowingConflictForSiblingDeclarations.test")
public void testNoShadowingConflictForSiblingDeclarations_NoShadowingConflictForSiblingDeclarations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/noShadowingConflictForSiblingDeclarations/noShadowingConflictForSiblingDeclarations.test");
@@ -438,6 +444,18 @@ public class RenameTestGenerated extends AbstractRenameTest {
doTest(fileName);
}
@TestMetadata("quotedToNonQuoted/quotedToNonQuoted.test")
public void testQuotedToNonQuoted_QuotedToNonQuoted() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/quotedToNonQuoted/quotedToNonQuoted.test");
doTest(fileName);
}
@TestMetadata("quotedToNonQuotedCaretAtTheEnd/quotedToNonQuotedCaretAtTheEnd.test")
public void testQuotedToNonQuotedCaretAtTheEnd_QuotedToNonQuotedCaretAtTheEnd() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/quotedToNonQuotedCaretAtTheEnd.test");
doTest(fileName);
}
@TestMetadata("renameArgumentsWhenParameterRenamed/parameter.test")
public void testRenameArgumentsWhenParameterRenamed_Parameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameArgumentsWhenParameterRenamed/parameter.test");