diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 44c0f6f6c31..9fe69ed6b9c 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -516,6 +516,7 @@
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AbstractReferenceSubstitutionRenameHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AbstractReferenceSubstitutionRenameHandler.kt
index 2859706fc03..f4cd44583a3 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AbstractReferenceSubstitutionRenameHandler.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AbstractReferenceSubstitutionRenameHandler.kt
@@ -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()
+ }
+
+ 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()
}
protected abstract fun getElementToRename(dataContext: DataContext): PsiElement?
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/KotlinMemberInplaceRenameHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/KotlinMemberInplaceRenameHandler.kt
new file mode 100644
index 00000000000..64afe17f98e
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/KotlinMemberInplaceRenameHandler.kt
@@ -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
+ }
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt
index 133d9d963e9..3c32a84ed5c 100644
--- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt
@@ -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()?.textOffset == offset
+ return elementAtCaret?.getNonStrictParentOfType()?.textOffset == offset
}
}
diff --git a/idea/testData/refactoring/rename/nonQuotedToQuoted/after/test.kt b/idea/testData/refactoring/rename/nonQuotedToQuoted/after/test.kt
new file mode 100644
index 00000000000..42ba390f4ed
--- /dev/null
+++ b/idea/testData/refactoring/rename/nonQuotedToQuoted/after/test.kt
@@ -0,0 +1,8 @@
+fun `fun`(n: Int) {
+
+}
+
+fun test() {
+ `fun`(1)
+ `fun`(2)
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/rename/nonQuotedToQuoted/before/test.kt b/idea/testData/refactoring/rename/nonQuotedToQuoted/before/test.kt
new file mode 100644
index 00000000000..27a5ee0a9e8
--- /dev/null
+++ b/idea/testData/refactoring/rename/nonQuotedToQuoted/before/test.kt
@@ -0,0 +1,8 @@
+fun /*rename*/foo(n: Int) {
+
+}
+
+fun test() {
+ foo(1)
+ foo(2)
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/rename/nonQuotedToQuoted/nonQuotedToQuoted.test b/idea/testData/refactoring/rename/nonQuotedToQuoted/nonQuotedToQuoted.test
new file mode 100644
index 00000000000..3125d1ea960
--- /dev/null
+++ b/idea/testData/refactoring/rename/nonQuotedToQuoted/nonQuotedToQuoted.test
@@ -0,0 +1,5 @@
+{
+ "type": "MARKED_ELEMENT",
+ "mainFile": "test.kt",
+ "newName": "`fun`"
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/rename/quotedToNonQuoted/after/test.kt b/idea/testData/refactoring/rename/quotedToNonQuoted/after/test.kt
new file mode 100644
index 00000000000..b701ef3f2dc
--- /dev/null
+++ b/idea/testData/refactoring/rename/quotedToNonQuoted/after/test.kt
@@ -0,0 +1,8 @@
+fun foo(n: Int) {
+
+}
+
+fun test() {
+ foo(1)
+ foo(2)
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/rename/quotedToNonQuoted/before/test.kt b/idea/testData/refactoring/rename/quotedToNonQuoted/before/test.kt
new file mode 100644
index 00000000000..2e95027d932
--- /dev/null
+++ b/idea/testData/refactoring/rename/quotedToNonQuoted/before/test.kt
@@ -0,0 +1,8 @@
+fun /*rename*/`fun`(n: Int) {
+
+}
+
+fun test() {
+ `fun`(1)
+ `fun`(2)
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/rename/quotedToNonQuoted/quotedToNonQuoted.test b/idea/testData/refactoring/rename/quotedToNonQuoted/quotedToNonQuoted.test
new file mode 100644
index 00000000000..0a3c57ce475
--- /dev/null
+++ b/idea/testData/refactoring/rename/quotedToNonQuoted/quotedToNonQuoted.test
@@ -0,0 +1,5 @@
+{
+ "type": "MARKED_ELEMENT",
+ "mainFile": "test.kt",
+ "newName": "foo"
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/after/test.kt b/idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/after/test.kt
new file mode 100644
index 00000000000..b701ef3f2dc
--- /dev/null
+++ b/idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/after/test.kt
@@ -0,0 +1,8 @@
+fun foo(n: Int) {
+
+}
+
+fun test() {
+ foo(1)
+ foo(2)
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/before/test.kt b/idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/before/test.kt
new file mode 100644
index 00000000000..378fdbd3199
--- /dev/null
+++ b/idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/before/test.kt
@@ -0,0 +1,8 @@
+fun `fun`/*rename*/(n: Int) {
+
+}
+
+fun test() {
+ `fun`(1)
+ `fun`(2)
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/quotedToNonQuotedCaretAtTheEnd.test b/idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/quotedToNonQuotedCaretAtTheEnd.test
new file mode 100644
index 00000000000..0a3c57ce475
--- /dev/null
+++ b/idea/testData/refactoring/rename/quotedToNonQuotedCaretAtTheEnd/quotedToNonQuotedCaretAtTheEnd.test
@@ -0,0 +1,5 @@
+{
+ "type": "MARKED_ELEMENT",
+ "mainFile": "test.kt",
+ "newName": "foo"
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java
index 321ed0621db..8c25c97482f 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java
@@ -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");