Rename: Support import aliases
#KT-4379 Fixed
This commit is contained in:
@@ -19,7 +19,9 @@ package org.jetbrains.kotlin.psi
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.psi.stubs.KotlinImportAliasStub
|
||||
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
|
||||
|
||||
@@ -31,6 +33,9 @@ class KtImportAlias : KtElementImplStub<KotlinImportAliasStub>, PsiNameIdentifie
|
||||
return visitor.visitImportAlias(this, data)
|
||||
}
|
||||
|
||||
val importDirective: KtImportDirective?
|
||||
get() = parent as? KtImportDirective
|
||||
|
||||
override fun getName() = stub?.getName() ?: nameIdentifier?.text
|
||||
|
||||
override fun setName(name: String): PsiElement {
|
||||
@@ -39,4 +44,8 @@ class KtImportAlias : KtElementImplStub<KotlinImportAliasStub>, PsiNameIdentifie
|
||||
}
|
||||
|
||||
override fun getNameIdentifier(): PsiElement? = findChildByType(KtTokens.IDENTIFIER)
|
||||
|
||||
override fun getTextOffset() = nameIdentifier?.textOffset ?: startOffset
|
||||
|
||||
override fun getUseScope() = LocalSearchScope(containingFile)
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.copied
|
||||
@@ -43,6 +45,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleReference<KtSimpleNameExpression>(expression) {
|
||||
@@ -235,4 +239,17 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
|
||||
|
||||
return listOf(element.getReferencedNameAsName())
|
||||
}
|
||||
|
||||
fun getImportAlias(): KtImportAlias? {
|
||||
val element = element
|
||||
val name = element.getReferencedName()
|
||||
val file = element.containingKtFile
|
||||
val importDirective = file.findImportByAlias(name) ?: return null
|
||||
val fqName = importDirective.importedFqName ?: return null
|
||||
val importedDescriptors = file.resolveImportReference(fqName)
|
||||
if (getTargetDescriptors(element.analyze(BodyResolveMode.PARTIAL)).any { it.getImportableDescriptor() in importedDescriptors }) {
|
||||
return importDirective.alias
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@ import com.intellij.psi.*
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
|
||||
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
||||
import org.jetbrains.kotlin.idea.kdoc.KDocReference
|
||||
@@ -35,6 +38,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.constant
|
||||
@@ -88,18 +92,22 @@ fun PsiReference.matchesTarget(candidateTarget: PsiElement): Boolean {
|
||||
}
|
||||
}
|
||||
|
||||
val targets = unwrappedTargets
|
||||
val element = element
|
||||
|
||||
val manager = candidateTarget.manager
|
||||
if (targets.any { manager.areElementsEquivalent(unwrappedCandidate, it) }) {
|
||||
return true
|
||||
if (candidateTarget is KtImportAlias && element is KtSimpleNameExpression && element.getReferencedName() == candidateTarget.name) {
|
||||
val importDirective = candidateTarget.importDirective ?: return false
|
||||
val importedFqName = importDirective.importedFqName ?: return false
|
||||
val importedDescriptors = importDirective.containingKtFile.resolveImportReference(importedFqName)
|
||||
val importableTargets = unwrappedTargets.mapNotNull {
|
||||
if (it is KtConstructor<*>) it.containingClassOrObject else it
|
||||
}
|
||||
return importedDescriptors.any { (it as? DeclarationDescriptorWithSource)?.source?.getPsi() in importableTargets }
|
||||
}
|
||||
|
||||
val element = element
|
||||
if (element is KtLabelReferenceExpression) {
|
||||
val labelParent = (element.parent as? KtContainerNode)?.parent
|
||||
when (labelParent) {
|
||||
is KtReturnExpression -> targets.forEach {
|
||||
is KtReturnExpression -> unwrappedTargets.forEach {
|
||||
if (it !is KtFunctionLiteral && !(it is KtNamedFunction && it.name.isNullOrEmpty())) return@forEach
|
||||
it as KtFunction
|
||||
|
||||
@@ -110,13 +118,19 @@ fun PsiReference.matchesTarget(candidateTarget: PsiElement): Boolean {
|
||||
val calleeReference = it.getCalleeByLambdaArgument()?.mainReference ?: return@forEach
|
||||
if (calleeReference.matchesTarget(candidateTarget)) return true
|
||||
}
|
||||
is KtBreakExpression, is KtContinueExpression -> targets.forEach {
|
||||
is KtBreakExpression, is KtContinueExpression -> unwrappedTargets.forEach {
|
||||
val labeledExpression = (it as? KtExpression)?.getLabeledParent(element.getReferencedName()) ?: return@forEach
|
||||
if (candidateTarget == labeledExpression) return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val targets = unwrappedTargets
|
||||
val manager = candidateTarget.manager
|
||||
if (targets.any { manager.areElementsEquivalent(unwrappedCandidate, it) }) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (this is KtReference) {
|
||||
return targets.any {
|
||||
it.isConstructorOf(unwrappedCandidate)
|
||||
|
||||
@@ -498,6 +498,7 @@
|
||||
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameBackingFieldReferenceHandler"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
|
||||
@@ -125,6 +125,7 @@ class KotlinElementDescriptionProvider : ElementDescriptionProvider {
|
||||
is KtDestructuringDeclarationEntry -> "variable"
|
||||
is KtTypeAlias -> "type alias"
|
||||
is KtLabeledExpression -> "label"
|
||||
is KtImportAlias -> "import alias"
|
||||
is RenameJavaSyntheticPropertyHandler.SyntheticPropertyWrapper -> "property"
|
||||
is KtLightClassForFacade -> "facade class"
|
||||
is RenameKotlinPropertyProcessor.PropertyMethodWrapper -> "property accessor"
|
||||
|
||||
@@ -80,6 +80,7 @@ class KotlinFindUsagesProvider : FindUsagesProvider {
|
||||
return funDescription + (element.containerDescription?.let { " of $it" } ?: "")
|
||||
}
|
||||
is KtLabeledExpression -> element.getLabelName() ?: ""
|
||||
is KtImportAlias -> element.getName() ?: ""
|
||||
is KtLightElement<*, *> -> element.kotlinOrigin?.let { getDescriptiveName(it) } ?: ""
|
||||
else -> ""
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
|
||||
return grandparent is KtCatchClause || grandparent is KtFunctionLiteral
|
||||
}
|
||||
}
|
||||
is KtLabeledExpression -> return true
|
||||
is KtLabeledExpression, is KtImportAlias -> return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.actionSystem.DataContext
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
class RenameImportAliasByReferenceHandler : AbstractReferenceSubstitutionRenameHandler(VariableInplaceRenameHandler()) {
|
||||
override fun getElementToRename(dataContext: DataContext): PsiElement? {
|
||||
val refExpr = getReferenceExpression(dataContext) as? KtSimpleNameExpression ?: return null
|
||||
return refExpr.mainReference.getImportAlias()
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||
import org.jetbrains.kotlin.idea.intentions.isAutoCreatedItUsage
|
||||
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.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
@@ -38,6 +39,7 @@ import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtilExtender {
|
||||
companion object {
|
||||
val DO_NOT_UNWRAP_LABELED_EXPRESSION = 0x100
|
||||
val BYPASS_IMPORT_ALIAS = 0x200
|
||||
|
||||
// Place caret after the open curly brace in lambda for generated 'it'
|
||||
fun findLambdaOpenLBraceForGeneratedIt(ref: PsiReference): PsiElement? {
|
||||
@@ -69,9 +71,9 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtil
|
||||
|
||||
override fun getAdditionalDefinitionSearchFlags() = 0
|
||||
|
||||
override fun getAdditionalReferenceSearchFlags() = DO_NOT_UNWRAP_LABELED_EXPRESSION
|
||||
override fun getAdditionalReferenceSearchFlags() = DO_NOT_UNWRAP_LABELED_EXPRESSION or BYPASS_IMPORT_ALIAS
|
||||
|
||||
override fun getAllAdditionalFlags() = DO_NOT_UNWRAP_LABELED_EXPRESSION
|
||||
override fun getAllAdditionalFlags() = additionalDefinitionSearchFlags + additionalReferenceSearchFlags
|
||||
|
||||
override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is KtClass && element.isAbstract())
|
||||
|
||||
@@ -84,6 +86,10 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtil
|
||||
return refTarget
|
||||
}
|
||||
|
||||
if (!BitUtil.isSet(flags, BYPASS_IMPORT_ALIAS)) {
|
||||
(ref.element as? KtSimpleNameExpression)?.mainReference?.getImportAlias()?.let { return it }
|
||||
}
|
||||
|
||||
// prefer destructing declaration entry to its target if element name is accepted
|
||||
if (ref is KtDestructuringDeclarationReference && BitUtil.isSet(flags, TargetElementUtil.ELEMENT_NAME_ACCEPTED)) {
|
||||
return ref.element
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
class Foo
|
||||
@@ -0,0 +1,5 @@
|
||||
import foo.Foo as Baz
|
||||
|
||||
fun test() {
|
||||
val bar: Baz = Baz()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
class Foo
|
||||
@@ -0,0 +1,5 @@
|
||||
import foo.Foo as /*rename*/Bar
|
||||
|
||||
fun test() {
|
||||
val bar: Bar = Bar()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "MARKED_ELEMENT",
|
||||
"mainFile": "test.kt",
|
||||
"newName": "Baz"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
class Foo
|
||||
@@ -0,0 +1,5 @@
|
||||
import foo.Foo as Baz
|
||||
|
||||
fun test() {
|
||||
val bar: Baz = Baz()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
class Foo
|
||||
@@ -0,0 +1,5 @@
|
||||
import foo.Foo as Bar
|
||||
|
||||
fun test() {
|
||||
val bar: /*rename*/Bar = Bar()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "AUTO_DETECT",
|
||||
"mainFile": "test.kt",
|
||||
"newName": "Baz"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import Foo as <info descr="null">~Bar</info>
|
||||
|
||||
class Foo
|
||||
|
||||
fun test() {
|
||||
val bar: <info descr="null">Bar</info> = <info descr="null">Bar</info>()
|
||||
}
|
||||
@@ -36,6 +36,12 @@ public class UsageHighlightingTestGenerated extends AbstractUsageHighlightingTes
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/usageHighlighter"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("importAlias.kt")
|
||||
public void testImportAlias() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/usageHighlighter/importAlias.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("labeledAnonymousFun.kt")
|
||||
public void testLabeledAnonymousFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/usageHighlighter/labeledAnonymousFun.kt");
|
||||
|
||||
@@ -204,6 +204,18 @@ public class RenameTestGenerated extends AbstractRenameTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("importAlias/importAlias.test")
|
||||
public void testImportAlias_ImportAlias() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/importAlias/importAlias.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("importAliasByRef/importAliasByRef.test")
|
||||
public void testImportAliasByRef_ImportAliasByRef() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/importAliasByRef/importAliasByRef.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("labeledAnonymousFunByLabel/labeledLambdaByLabel.test")
|
||||
public void testLabeledAnonymousFunByLabel_LabeledLambdaByLabel() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/labeledAnonymousFunByLabel/labeledLambdaByLabel.test");
|
||||
|
||||
Reference in New Issue
Block a user