Rename: Fix inplace refactoring for declarations with backticked names
#KT-15859 Fixed
This commit is contained in:
@@ -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"/>
|
||||
|
||||
+13
-7
@@ -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?
|
||||
|
||||
+69
@@ -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
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -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)
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fun `fun`/*rename*/(n: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
`fun`(1)
|
||||
`fun`(2)
|
||||
}
|
||||
Vendored
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user