KT-4945 Intention "Introduce static import" (for single symbol)
#KT-4945 Fixed
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
import javax.swing.SwingUtilities
|
||||||
|
<spot>import javax.swing.SwingUtilities.invokeLater</spot>
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
<spot>invokeLater</spot> { }
|
||||||
|
<spot>invokeLater</spot>()
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
<spot>SwingUtilities.invokeLater</spot> { }
|
||||||
|
<spot>SwingUtilities.invokeLater</spot>()
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This intention converts qualified references to a Java static member, object member or enum class member
|
||||||
|
into simple references and adds import directive for that symbol.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -627,6 +627,11 @@
|
|||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
</intentionAction>
|
</intentionAction>
|
||||||
|
|
||||||
|
<intentionAction>
|
||||||
|
<className>org.jetbrains.kotlin.idea.intentions.ImportMemberIntention</className>
|
||||||
|
<category>Kotlin</category>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
<intentionAction>
|
<intentionAction>
|
||||||
<className>org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention</className>
|
<className>org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention</className>
|
||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 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.intentions
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
|
||||||
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
|
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||||
|
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
|
||||||
|
class ImportMemberIntention : SelfTargetingOffsetIndependentIntention<KtNameReferenceExpression>(
|
||||||
|
KtNameReferenceExpression::class.java,
|
||||||
|
"Add import for member"
|
||||||
|
){
|
||||||
|
override fun isApplicableTo(element: KtNameReferenceExpression): Boolean {
|
||||||
|
val qualifiedExpression = qualifiedExpression(element) ?: return false
|
||||||
|
|
||||||
|
val fqName = targetFqName(qualifiedExpression) ?: return false
|
||||||
|
|
||||||
|
text = "Add import for '${fqName.asString()}'"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyTo(element: KtNameReferenceExpression, editor: Editor) {
|
||||||
|
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val targets = element.mainReference.resolveToDescriptors(bindingContext)
|
||||||
|
val fqName = targets.map { it.importableFqName!! }.single()
|
||||||
|
|
||||||
|
val file = element.getContainingKtFile()
|
||||||
|
val helper = ImportInsertHelper.getInstance(element.project)
|
||||||
|
if (helper.importDescriptor(file, targets.first()) == ImportInsertHelper.ImportDescriptorResult.FAIL) return
|
||||||
|
|
||||||
|
val qualifiedExpressions = file.collectDescendantsOfType<KtDotQualifiedExpression> { qualifiedExpression ->
|
||||||
|
val selector = qualifiedExpression.getQualifiedElementSelector() as? KtNameReferenceExpression
|
||||||
|
?: return@collectDescendantsOfType false
|
||||||
|
val qualifierName = qualifiedExpression.receiverExpression.getQualifiedElementSelector() as? KtNameReferenceExpression
|
||||||
|
?: return@collectDescendantsOfType false
|
||||||
|
selector.getReferencedNameAsName() == fqName.shortName()
|
||||||
|
&& qualifierName.getReferencedNameAsName() == fqName.parent().shortName()
|
||||||
|
&& targetFqName(qualifiedExpression) == fqName
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: not deep
|
||||||
|
ShortenReferences.DEFAULT.process(qualifiedExpressions)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun qualifiedExpression(element: KtNameReferenceExpression): KtDotQualifiedExpression? {
|
||||||
|
val parent = element.parent
|
||||||
|
return (if (parent is KtCallExpression) parent.parent else parent) as? KtDotQualifiedExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun targetFqName(qualifiedExpression: KtDotQualifiedExpression): FqName? {
|
||||||
|
val nameExpression = qualifiedExpression.getQualifiedElementSelector() as? KtNameReferenceExpression ?: return null
|
||||||
|
val bindingContext = qualifiedExpression.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
if (bindingContext[BindingContext.QUALIFIER, qualifiedExpression.receiverExpression] == null) return null
|
||||||
|
|
||||||
|
val targets = nameExpression.mainReference.resolveToDescriptors(bindingContext)
|
||||||
|
if (targets.isEmpty()) return null
|
||||||
|
if (!targets.all { it.canBeReferencedViaImport() }) return null
|
||||||
|
return targets.map { it.importableFqName }.singleOrNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.intentions.ImportMemberIntention
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'kotlin.LazyThreadSafetyMode.NONE'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val v1: Int by lazy(LazyThreadSafetyMode.NONE<caret>) { 1 }
|
||||||
|
val v2: Int by lazy(LazyThreadSafetyMode.NONE) { 1 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import kotlin.LazyThreadSafetyMode.NONE
|
||||||
|
|
||||||
|
// INTENTION_TEXT: "Add import for 'kotlin.LazyThreadSafetyMode.NONE'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val v1: Int by lazy(NONE<caret>) { 1 }
|
||||||
|
val v2: Int by lazy(NONE) { 1 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// ERROR: Unresolved reference: xxx
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
SwingUtilities.<caret>xxx {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'kotlin.properties.Delegates.notNull'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val v1: Int by Delegates.notNull()
|
||||||
|
val v2: Char by Delegates.notNull<caret>()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'kotlin.properties.Delegates.notNull'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
import kotlin.properties.Delegates.notNull
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val v1: Int by notNull()
|
||||||
|
val v2: Char by notNull<caret>()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'javax.swing.SwingConstants.CENTER'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val v = SwingConstants.CENTER
|
||||||
|
|
||||||
|
SwingConstants.<caret>CENTER
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'javax.swing.SwingConstants.CENTER'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import javax.swing.SwingConstants
|
||||||
|
import javax.swing.SwingConstants.CENTER
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val v = CENTER
|
||||||
|
|
||||||
|
<caret>CENTER
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'javax.swing.SwingUtilities.invokeLater'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// ERROR: No value passed for parameter p0
|
||||||
|
// ERROR: Unresolved reference: SomethingElse
|
||||||
|
// ERROR: Unresolved reference: somethingElse
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
SwingUtilities.<caret>invokeLater()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
javax.swing.SwingUtilities.invokeLater {
|
||||||
|
}
|
||||||
|
|
||||||
|
javax.swing.SwingUtilities.invokeLater(Runnable {
|
||||||
|
SwingUtilities.invokeLater { }
|
||||||
|
})
|
||||||
|
|
||||||
|
SwingUtilities.invokeAndWait { }
|
||||||
|
|
||||||
|
SomethingElse.invokeLater()
|
||||||
|
|
||||||
|
somethingElse.SwingUtilities.invokeLater()
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'javax.swing.SwingUtilities.invokeLater'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// ERROR: No value passed for parameter p0
|
||||||
|
// ERROR: Unresolved reference: SomethingElse
|
||||||
|
// ERROR: Unresolved reference: somethingElse
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
import javax.swing.SwingUtilities.invokeLater
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
<caret>invokeLater()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
invokeLater {
|
||||||
|
}
|
||||||
|
|
||||||
|
invokeLater(Runnable {
|
||||||
|
invokeLater { }
|
||||||
|
})
|
||||||
|
|
||||||
|
SwingUtilities.invokeAndWait { }
|
||||||
|
|
||||||
|
SomethingElse.invokeLater()
|
||||||
|
|
||||||
|
somethingElse.SwingUtilities.invokeLater()
|
||||||
|
}
|
||||||
@@ -5098,6 +5098,45 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/intentions/importMember")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ImportMember extends AbstractIntentionTest {
|
||||||
|
public void testAllFilesPresentInImportMember() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/importMember"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("EnumMember.kt")
|
||||||
|
public void testEnumMember() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/EnumMember.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("NoTarget.kt")
|
||||||
|
public void testNoTarget() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/NoTarget.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ObjectMethod.kt")
|
||||||
|
public void testObjectMethod() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/ObjectMethod.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("StaticJavaField.kt")
|
||||||
|
public void testStaticJavaField() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/StaticJavaField.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("StaticJavaMethod.kt")
|
||||||
|
public void testStaticJavaMethod() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/StaticJavaMethod.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/intentions/infixCallToOrdinary")
|
@TestMetadata("idea/testData/intentions/infixCallToOrdinary")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user