FIR IDE: Add ImportAllMembersIntention
This commit is contained in:
committed by
TeamCityServer
parent
8d69f32d04
commit
66d44f2471
+1
-1
@@ -1028,7 +1028,6 @@ fun main(args: Array<String>) {
|
||||
model("checker/diagnosticsMessage", excludedPattern = excludedFirTestdataPattern)
|
||||
}
|
||||
|
||||
|
||||
testClass<AbstractHighLevelQuickFixTest> {
|
||||
val pattern = "^([\\w\\-_]+)\\.kt$"
|
||||
model("quickfix/abstract", pattern = pattern, filenameStartsLowerCase = true)
|
||||
@@ -1067,6 +1066,7 @@ fun main(args: Array<String>) {
|
||||
val pattern = "^([\\w\\-_]+)\\.(kt|kts)$"
|
||||
model("intentions/addPropertyAccessors", pattern = pattern)
|
||||
model("intentions/specifyTypeExplicitly", pattern = pattern)
|
||||
model("intentions/importAllMembers", pattern = pattern)
|
||||
}
|
||||
|
||||
testClass<AbstractFirShortenRefsTest> {
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.fir.intentions
|
||||
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.api.applicator.HLApplicatorInput
|
||||
import org.jetbrains.kotlin.idea.api.applicator.applicator
|
||||
import org.jetbrains.kotlin.idea.fir.api.AbstractHLIntention
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicabilityRange
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicatorInputProvider
|
||||
import org.jetbrains.kotlin.idea.fir.api.applicator.inputProvider
|
||||
import org.jetbrains.kotlin.idea.fir.applicators.ApplicabilityRanges
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.calls.getSuccessCallSymbolOrNull
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.ShortenCommand
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.ShortenOption
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
||||
import org.jetbrains.kotlin.idea.references.KtReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isInImportDirective
|
||||
|
||||
class HLImportAllMembersIntention :
|
||||
AbstractHLIntention<KtExpression, HLImportAllMembersIntention.Input>(KtExpression::class, Companion.applicator), HighPriorityAction {
|
||||
override val applicabilityRange: HLApplicabilityRange<KtExpression> get() = ApplicabilityRanges.SELF
|
||||
|
||||
override val inputProvider: HLApplicatorInputProvider<KtExpression, Input> = inputProvider { psi ->
|
||||
val target = psi.actualReference?.resolveToSymbol() as? KtNamedClassOrObjectSymbol ?: return@inputProvider null
|
||||
val classId = target.classIdIfNonLocal ?: return@inputProvider null
|
||||
if (target.origin != KtSymbolOrigin.JAVA &&
|
||||
(target.classKind == KtClassKind.OBJECT ||
|
||||
// One cannot use on-demand import for properties or functions declared inside objects
|
||||
isReferenceToObjectMemberOrUnresolved(psi))
|
||||
) {
|
||||
// Import all members of an object is not supported by Kotlin.
|
||||
return@inputProvider null
|
||||
}
|
||||
val shortenCommand = collectPossibleReferenceShortenings(
|
||||
psi.containingKtFile,
|
||||
classShortenOption = {
|
||||
if (it.classIdIfNonLocal?.isNestedClassIn(classId) == true) {
|
||||
ShortenOption.SHORTEN_AND_STAR_IMPORT
|
||||
} else {
|
||||
ShortenOption.DO_NOT_SHORTEN
|
||||
}
|
||||
},
|
||||
callableShortenOption = {
|
||||
val containingClassId = if (it is KtConstructorSymbol) {
|
||||
it.containingClassIdIfNonLocal?.outerClassId
|
||||
} else {
|
||||
it.callableIdIfNonLocal?.classId
|
||||
}
|
||||
if (containingClassId == classId) {
|
||||
ShortenOption.SHORTEN_AND_STAR_IMPORT
|
||||
} else {
|
||||
ShortenOption.DO_NOT_SHORTEN
|
||||
}
|
||||
}
|
||||
)
|
||||
if (shortenCommand.isEmpty) return@inputProvider null
|
||||
Input(classId.asSingleFqName(), shortenCommand)
|
||||
}
|
||||
|
||||
private fun ClassId.isNestedClassIn(classId: ClassId) =
|
||||
packageFqName == classId.packageFqName && relativeClassName.parent() == classId.relativeClassName
|
||||
|
||||
class Input(val fqName: FqName, val shortenCommand: ShortenCommand) : HLApplicatorInput
|
||||
|
||||
companion object {
|
||||
val applicator = applicator<KtExpression, Input> {
|
||||
familyName(KotlinBundle.lazyMessage("import.members.with"))
|
||||
actionName { _, input -> KotlinBundle.message("import.members.from.0", input.fqName.asString()) }
|
||||
isApplicableByPsi { it.isOnTheLeftOfQualificationDot && !it.isInImportDirective() }
|
||||
applyTo { _, input ->
|
||||
input.shortenCommand.invokeShortening()
|
||||
}
|
||||
}
|
||||
|
||||
private val KtExpression.isOnTheLeftOfQualificationDot: Boolean
|
||||
get() {
|
||||
return when (val parent = parent) {
|
||||
is KtDotQualifiedExpression -> this == parent.receiverExpression
|
||||
is KtUserType -> {
|
||||
val grandParent = parent.parent as? KtUserType ?: return false
|
||||
grandParent.qualifier == parent && parent.referenceExpression == this
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private val KtExpression.actualReference: KtReference?
|
||||
get() = when (this) {
|
||||
is KtDotQualifiedExpression -> selectorExpression?.mainReference ?: mainReference
|
||||
else -> mainReference
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.isReferenceToObjectMemberOrUnresolved(qualifiedAccess: KtExpression): Boolean {
|
||||
val selectorExpression: KtExpression? = qualifiedAccess.getQualifiedExpressionForReceiver()?.selectorExpression
|
||||
val referencedSymbol = when (selectorExpression) {
|
||||
is KtCallExpression -> selectorExpression.resolveCall()?.targetFunction?.getSuccessCallSymbolOrNull()
|
||||
is KtNameReferenceExpression -> selectorExpression.mainReference.resolveToSymbol()
|
||||
else -> return false
|
||||
} as? KtSymbolWithKind ?: return true
|
||||
if (referencedSymbol is KtConstructorSymbol) return false
|
||||
return (referencedSymbol.getContainingSymbol() as? KtClassOrObjectSymbol)?.classKind?.isObject ?: true
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+83
@@ -473,4 +473,87 @@ public class HLIntentionTestGenerated extends AbstractHLIntentionTest {
|
||||
runTest("idea/testData/intentions/specifyTypeExplicitly/unitType.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/importAllMembers")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ImportAllMembers extends AbstractHLIntentionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInImportAllMembers() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/intentions/importAllMembers"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("AlreadyImported.kt")
|
||||
public void testAlreadyImported() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/AlreadyImported.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("AlreadyImportedWithStar.kt")
|
||||
public void testAlreadyImportedWithStar() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/AlreadyImportedWithStar.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("AmbiguousCalls.kt")
|
||||
public void testAmbiguousCalls() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/AmbiguousCalls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("EnumMembers.kt")
|
||||
public void testEnumMembers() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/EnumMembers.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ImportAllMembersInImport.kt")
|
||||
public void testImportAllMembersInImport() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/ImportAllMembersInImport.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NotFromCompanionObject.kt")
|
||||
public void testNotFromCompanionObject() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/NotFromCompanionObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NotFromObject.kt")
|
||||
public void testNotFromObject() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/NotFromObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("QualifiedName.kt")
|
||||
public void testQualifiedName() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/QualifiedName.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("QualifiedName2.kt")
|
||||
public void testQualifiedName2() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/QualifiedName2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("RemoveSingleImports.kt")
|
||||
public void testRemoveSingleImports() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/RemoveSingleImports.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("StaticJavaMembers.kt")
|
||||
public void testStaticJavaMembers() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/StaticJavaMembers.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TypeReference.kt")
|
||||
public void testTypeReference() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/TypeReference.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TypeReference2.kt")
|
||||
public void testTypeReference2() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/TypeReference2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnresolvedMember.kt")
|
||||
public void testUnresolvedMember() throws Exception {
|
||||
runTest("idea/testData/intentions/importAllMembers/UnresolvedMember.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import javax.swing.SwingUtilities
|
||||
<spot>import javax.swing.SwingUtilities.*</spot>
|
||||
|
||||
fun foo() {
|
||||
SwingUtilities.invokeLater { }
|
||||
SwingUtilities.invokeAndWait()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
fun foo() {
|
||||
<spot>SwingUtilities.</spot>invokeLater { }
|
||||
<spot>SwingUtilities.</spot>invokeAndWait()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts the selected qualified reference into a simple reference and adds an import for the corresponding class.
|
||||
</body>
|
||||
</html>
|
||||
@@ -19,5 +19,10 @@
|
||||
<className>org.jetbrains.kotlin.idea.fir.intentions.HLAddSetterIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.fir.intentions.HLImportAllMembersIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.fir.intentions.HLImportAllMembersIntention
|
||||
Reference in New Issue
Block a user