Smart completion for "ClassName::class" and "ClassName::class.java" when specific KClass or Class expected

This commit is contained in:
Valentin Kipyatkov
2015-10-08 13:42:02 +03:00
parent b92a06929e
commit 2c1defaf18
12 changed files with 193 additions and 0 deletions
@@ -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.completion.smart
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
import org.jetbrains.kotlin.idea.completion.LookupElementFactory
import org.jetbrains.kotlin.idea.completion.createLookupElementForType
import org.jetbrains.kotlin.idea.completion.fuzzyType
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.quickfix.moveCaret
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.Variance
import java.util.*
object ClassLiteralItems {
public fun addToCollection(
collection: MutableCollection<LookupElement>,
expectedInfos: Collection<ExpectedInfo>,
lookupElementFactory: LookupElementFactory,
isJvmModule: Boolean
) {
val typeAndSuffixToExpectedInfos = LinkedHashMap<Pair<JetType, String>, MutableList<ExpectedInfo>>()
for (expectedInfo in expectedInfos) {
val fuzzyType = expectedInfo.fuzzyType ?: continue
if (fuzzyType.freeParameters.isNotEmpty()) continue
val typeConstructor = fuzzyType.type.constructor
val klass = typeConstructor.declarationDescriptor as? ClassDescriptor ?: continue
val typeArgument = fuzzyType.type.arguments.singleOrNull() ?: continue
if (typeArgument.projectionKind != Variance.INVARIANT) continue
if (KotlinBuiltIns.isKClass(klass)) {
typeAndSuffixToExpectedInfos.getOrPut(typeArgument.type to "::class") { ArrayList() }.add(expectedInfo)
}
if (isJvmModule && klass.importableFqName?.asString() == "java.lang.Class") {
typeAndSuffixToExpectedInfos.getOrPut(typeArgument.type to "::class.java") { ArrayList() }.add(expectedInfo)
}
}
for ((pair, matchedExpectedInfos) in typeAndSuffixToExpectedInfos) {
val (type, suffix) = pair
var lookupElement = lookupElementFactory.createLookupElementForType(type) ?: continue
val text = lookupElement.lookupString + suffix
lookupElement = object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun getLookupString() = text
override fun getAllLookupStrings() = setOf(lookupString)
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.itemText = text
}
override fun handleInsert(context: InsertionContext) {
super.handleInsert(context)
val offset = context.tailOffset
context.document.insertString(offset, suffix)
context.editor.moveCaret(offset + suffix.length())
}
}
lookupElement.assignSmartCompletionPriority(SmartCompletionItemPriority.CLASS_LITERAL)
lookupElement = lookupElement.addTailAndNameSimilarity(matchedExpectedInfos, emptyList())
collection.add(lookupElement)
}
}
}
@@ -207,6 +207,8 @@ class SmartCompletion(
StaticMembers(bindingContext, lookupElementFactory).addToCollection(items, expectedInfos, expression, descriptorsToSkip)
}
ClassLiteralItems.addToCollection(items, expectedInfos, lookupElementFactory, isJvmModule)
if (!forBasicCompletion) {
LambdaItems.addToCollection(items, expectedInfos)
@@ -0,0 +1,5 @@
open class A<T : Any>(val javaClass: Class<T>?)
class B : A<java.io.File>(<caret>)
// ELEMENT: "File::class.java"
@@ -0,0 +1,7 @@
import java.io.File
open class A<T : Any>(val javaClass: Class<T>?)
class B : A<java.io.File>(File::class.java)<caret>
// ELEMENT: "File::class.java"
@@ -0,0 +1,7 @@
import kotlin.reflect.KClass
open class A<T : Any>(val kClass: KClass<T>?)
class B : A<java.io.File>(<caret>)
// ELEMENT: "File::class"
@@ -0,0 +1,8 @@
import java.io.File
import kotlin.reflect.KClass
open class A<T : Any>(val kClass: KClass<T>?)
class B : A<java.io.File>(File::class)<caret>
// ELEMENT: "File::class"
@@ -0,0 +1,5 @@
open class A<T : Any>(val javaClass: Class<T>)
class B : A<String>(<caret>)
// EXIST_JAVA_ONLY: { lookupString: "String::class.java", itemText: "String::class.java", attributes: "" }
@@ -0,0 +1,7 @@
import kotlin.reflect.KClass
open class A<T : Any>(val kClass: KClass<T>)
class B : A<String>(<caret>)
// EXIST: { lookupString: "String::class", itemText: "String::class", attributes: "" }
@@ -0,0 +1,14 @@
import kotlin.reflect.KClass
fun <T : Any> foo(kClass: KClass<T>){}
fun <T : Any> foo(kClass: KClass<T>?, p: Int){}
fun f() {
foo<Int>(<caret>)
}
// WITH_ORDER
// EXIST: { lookupString: "Int::class", itemText: "Int::class", attributes: "" }
// EXIST: { lookupString: "object" }
// EXIST: null
// NOTHING_ELSE
@@ -0,0 +1,16 @@
import kotlin.reflect.KClass
interface I
fun foo(kClass: KClass<out I>){}
fun foo(kClass: KClass<in I>, p: Int){}
fun foo(kClass: KClass<*>, c: Char){}
fun <T : Any> foo(kClass: KClass<T>, d: Double){}
fun bar() {
foo(<caret>)
}
// ABSENT: "I::class"
// ABSENT: "Any::class"
// ABSENT: "T::class"
@@ -625,6 +625,24 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName);
}
@TestMetadata("ConcreteJavaClassExpected.kt")
public void testConcreteJavaClassExpected() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/ConcreteJavaClassExpected.kt");
doTest(fileName);
}
@TestMetadata("ConcreteKClassExpected.kt")
public void testConcreteKClassExpected() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpected.kt");
doTest(fileName);
}
@TestMetadata("ConcreteKClassExpectedNoDuplicates.kt")
public void testConcreteKClassExpectedNoDuplicates() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpectedNoDuplicates.kt");
doTest(fileName);
}
@TestMetadata("EmptyQualifier1.kt")
public void testEmptyQualifier1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/EmptyQualifier1.kt");
@@ -703,6 +721,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName);
}
@TestMetadata("NonConcreteKClassExpected.kt")
public void testNonConcreteKClassExpected() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/NonConcreteKClassExpected.kt");
doTest(fileName);
}
@TestMetadata("NonEmptyQualifier1.kt")
public void testNonEmptyQualifier1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/NonEmptyQualifier1.kt");
@@ -263,6 +263,18 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest(fileName);
}
@TestMetadata("ConcreteJavaClass.kt")
public void testConcreteJavaClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/ConcreteJavaClass.kt");
doTest(fileName);
}
@TestMetadata("ConcreteKClass.kt")
public void testConcreteKClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/ConcreteKClass.kt");
doTest(fileName);
}
@TestMetadata("Constructor.kt")
public void testConstructor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/Constructor.kt");