diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ClassLiteralItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ClassLiteralItems.kt new file mode 100644 index 00000000000..a52df9e1ab5 --- /dev/null +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ClassLiteralItems.kt @@ -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, + expectedInfos: Collection, + lookupElementFactory: LookupElementFactory, + isJvmModule: Boolean + ) { + val typeAndSuffixToExpectedInfos = LinkedHashMap, MutableList>() + + 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) { + 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) + } + } +} \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index 6a0ee43874e..7e7dbf5af8f 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -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) diff --git a/idea/idea-completion/testData/handlers/smart/ConcreteJavaClass.kt b/idea/idea-completion/testData/handlers/smart/ConcreteJavaClass.kt new file mode 100644 index 00000000000..b967ac87719 --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/ConcreteJavaClass.kt @@ -0,0 +1,5 @@ +open class A(val javaClass: Class?) + +class B : A() + +// ELEMENT: "File::class.java" diff --git a/idea/idea-completion/testData/handlers/smart/ConcreteJavaClass.kt.after b/idea/idea-completion/testData/handlers/smart/ConcreteJavaClass.kt.after new file mode 100644 index 00000000000..354c4f50f9c --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/ConcreteJavaClass.kt.after @@ -0,0 +1,7 @@ +import java.io.File + +open class A(val javaClass: Class?) + +class B : A(File::class.java) + +// ELEMENT: "File::class.java" diff --git a/idea/idea-completion/testData/handlers/smart/ConcreteKClass.kt b/idea/idea-completion/testData/handlers/smart/ConcreteKClass.kt new file mode 100644 index 00000000000..4f19f582cf3 --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/ConcreteKClass.kt @@ -0,0 +1,7 @@ +import kotlin.reflect.KClass + +open class A(val kClass: KClass?) + +class B : A() + +// ELEMENT: "File::class" diff --git a/idea/idea-completion/testData/handlers/smart/ConcreteKClass.kt.after b/idea/idea-completion/testData/handlers/smart/ConcreteKClass.kt.after new file mode 100644 index 00000000000..e2f4c5d7f39 --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/ConcreteKClass.kt.after @@ -0,0 +1,8 @@ +import java.io.File +import kotlin.reflect.KClass + +open class A(val kClass: KClass?) + +class B : A(File::class) + +// ELEMENT: "File::class" diff --git a/idea/idea-completion/testData/smart/callableReference/ConcreteJavaClassExpected.kt b/idea/idea-completion/testData/smart/callableReference/ConcreteJavaClassExpected.kt new file mode 100644 index 00000000000..1a05d771015 --- /dev/null +++ b/idea/idea-completion/testData/smart/callableReference/ConcreteJavaClassExpected.kt @@ -0,0 +1,5 @@ +open class A(val javaClass: Class) + +class B : A() + +// EXIST_JAVA_ONLY: { lookupString: "String::class.java", itemText: "String::class.java", attributes: "" } diff --git a/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpected.kt b/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpected.kt new file mode 100644 index 00000000000..497e763ac3b --- /dev/null +++ b/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpected.kt @@ -0,0 +1,7 @@ +import kotlin.reflect.KClass + +open class A(val kClass: KClass) + +class B : A() + +// EXIST: { lookupString: "String::class", itemText: "String::class", attributes: "" } diff --git a/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpectedNoDuplicates.kt b/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpectedNoDuplicates.kt new file mode 100644 index 00000000000..312571ba0f3 --- /dev/null +++ b/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpectedNoDuplicates.kt @@ -0,0 +1,14 @@ +import kotlin.reflect.KClass + +fun foo(kClass: KClass){} +fun foo(kClass: KClass?, p: Int){} + +fun f() { + foo() +} + +// WITH_ORDER +// EXIST: { lookupString: "Int::class", itemText: "Int::class", attributes: "" } +// EXIST: { lookupString: "object" } +// EXIST: null +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/smart/callableReference/NonConcreteKClassExpected.kt b/idea/idea-completion/testData/smart/callableReference/NonConcreteKClassExpected.kt new file mode 100644 index 00000000000..5233e25cfb4 --- /dev/null +++ b/idea/idea-completion/testData/smart/callableReference/NonConcreteKClassExpected.kt @@ -0,0 +1,16 @@ +import kotlin.reflect.KClass + +interface I + +fun foo(kClass: KClass){} +fun foo(kClass: KClass, p: Int){} +fun foo(kClass: KClass<*>, c: Char){} +fun foo(kClass: KClass, d: Double){} + +fun bar() { + foo() +} + +// ABSENT: "I::class" +// ABSENT: "Any::class" +// ABSENT: "T::class" diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java index 9330ad7a1cb..f5ec5a65bd0 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java @@ -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"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java index 00cf7dc2610..54b70c481cd 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java @@ -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");