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>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ImportMemberIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention</className>
|
||||
<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")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user