diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt index b7d51892ac7..afe9c3cdc4a 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt @@ -55,6 +55,7 @@ public class ShadowedDeclarationsFilter private constructor( is CallTypeAndReceiver.DOT -> callTypeAndReceiver.receiver is CallTypeAndReceiver.SAFE -> callTypeAndReceiver.receiver is CallTypeAndReceiver.INFIX -> callTypeAndReceiver.receiver + is CallTypeAndReceiver.TYPE -> null // need filtering of classes with the same FQ-name else -> return null // TODO: support shadowed declarations filtering for callable references } @@ -98,7 +99,8 @@ public class ShadowedDeclarationsFilter private constructor( private fun signature(descriptor: DeclarationDescriptor): Any { return when (descriptor) { is SimpleFunctionDescriptor -> FunctionSignature(descriptor) - is VariableDescriptor -> descriptor.getName() + is VariableDescriptor -> descriptor.name + is ClassDescriptor -> descriptor.importableFqName ?: descriptor else -> descriptor } } @@ -109,9 +111,14 @@ public class ShadowedDeclarationsFilter private constructor( descriptors: Collection, descriptorsToImport: Collection = emptyList() ): Collection { - if (descriptors.size() == 1) return descriptors + if (descriptors.size == 1) return descriptors val first = descriptors.first() + + if (first is ClassDescriptor) { // for classes with the same FQ-name we simply take the first one + return listOf(first) + } + val isFunction = first is FunctionDescriptor val name = first.getName() val parameters = (first as CallableDescriptor).getValueParameters() diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt index b416f49e1d2..113a8f79771 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt @@ -39,8 +39,13 @@ class AllClassesCompletion(private val parameters: CompletionParameters, ) { fun collect(classDescriptorCollector: (ClassDescriptor) -> Unit, javaClassCollector: (PsiClass) -> Unit) { - //TODO: this is a temporary hack until we have built-ins in indices - collectClassesFromScope(classDescriptorCollector, resolutionFacade.moduleDescriptor.builtIns.builtInsPackageScope) + //TODO: this is a temporary solution until we have built-ins in indices + // we need only nested classes because top-level built-ins are all added through default imports + collectClassesFromScope(resolutionFacade.moduleDescriptor.builtIns.builtInsPackageScope) { + if (it.containingDeclaration is ClassDescriptor) { + classDescriptorCollector(it) + } + } kotlinIndicesHelper .getKotlinClasses({ prefixMatcher.prefixMatches(it) }, kindFilter) @@ -51,14 +56,14 @@ class AllClassesCompletion(private val parameters: CompletionParameters, } } - private fun collectClassesFromScope(collector: (ClassDescriptor) -> Unit, scope: JetScope) { + private fun collectClassesFromScope(scope: JetScope, collector: (ClassDescriptor) -> Unit) { for (descriptor in scope.getDescriptorsFiltered(DescriptorKindFilter.CLASSIFIERS)) { if (descriptor is ClassDescriptor) { if (kindFilter(descriptor.kind) && prefixMatcher.prefixMatches(descriptor.name.asString())) { collector(descriptor) } - collectClassesFromScope(collector, descriptor.unsubstitutedInnerClassesScope) + collectClassesFromScope(descriptor.unsubstitutedInnerClassesScope, collector) } } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index 19b7fd42f23..01292b8db6d 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -53,13 +53,15 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance class CompletionSessionConfiguration( val completeNonImportedDeclarations: Boolean, val completeNonAccessibleDeclarations: Boolean, - val filterOutJavaGettersAndSetters: Boolean + val filterOutJavaGettersAndSetters: Boolean, + val completeJavaClassesNotToBeUsed: Boolean ) fun CompletionSessionConfiguration(parameters: CompletionParameters) = CompletionSessionConfiguration( - completeNonImportedDeclarations = parameters.getInvocationCount() >= 2, - completeNonAccessibleDeclarations = parameters.getInvocationCount() >= 2, - filterOutJavaGettersAndSetters = parameters.getInvocationCount() < 2 + completeNonImportedDeclarations = parameters.invocationCount >= 2, + completeNonAccessibleDeclarations = parameters.invocationCount >= 2, + filterOutJavaGettersAndSetters = parameters.invocationCount < 2, + completeJavaClassesNotToBeUsed = parameters.invocationCount >= 2 ) abstract class CompletionSession(protected val configuration: CompletionSessionConfiguration, @@ -150,7 +152,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC protected val toFromOriginalFileMapper: ToFromOriginalFileMapper = ToFromOriginalFileMapper(parameters.originalFile as JetFile, position.containingFile as JetFile, parameters.offset) - protected fun isVisibleDescriptor(descriptor: DeclarationDescriptor): Boolean { + private fun isVisibleDescriptor(descriptor: DeclarationDescriptor): Boolean { + if (!configuration.completeJavaClassesNotToBeUsed && descriptor is ClassDescriptor) { + val classification = descriptor.importableFqName?.let { importableFqNameClassifier.classify(it, isPackage = false) } + if (classification == ImportableFqNameClassifier.Classification.notToBeUsedInKotlin) return false + } + if (descriptor is TypeParameterDescriptor && !isTypeParameterVisible(descriptor)) return false if (descriptor is DeclarationDescriptorWithVisibility) { @@ -199,11 +206,11 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC protected abstract val expectedInfos: Collection + protected val importableFqNameClassifier = ImportableFqNameClassifier(file) + protected open fun createSorter(): CompletionSorter { var sorter = CompletionSorter.defaultSorter(parameters, prefixMatcher)!! - val importableFqNameClassifier = ImportableFqNameClassifier(file) - sorter = sorter.weighBefore("stats", DeprecatedWeigher, PriorityWeigher, NotImportedWeigher(importableFqNameClassifier), KindWeigher, CallableWeigher) sorter = sorter.weighAfter("stats", VariableOrFunctionWeigher, ImportedWeigher(importableFqNameClassifier)) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt index 1a17f4e9d8d..3e2ffdc278f 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt @@ -261,9 +261,11 @@ public class KotlinCompletionContributor : CompletionContributor() { val somethingAdded = session.complete() if (!somethingAdded && parameters.getInvocationCount() < 2) { // Rerun completion if nothing was found - val newConfiguration = CompletionSessionConfiguration(completeNonImportedDeclarations = true, - completeNonAccessibleDeclarations = false, - filterOutJavaGettersAndSetters = false) + val newConfiguration = CompletionSessionConfiguration( + completeNonImportedDeclarations = true, + completeNonAccessibleDeclarations = false, + filterOutJavaGettersAndSetters = false, + completeJavaClassesNotToBeUsed = false) BasicCompletionSession(newConfiguration, parameters, result).complete() } } diff --git a/idea/idea-completion/testData/basic/common/annotations/Deprecated.kt b/idea/idea-completion/testData/basic/common/annotations/Deprecated.kt new file mode 100644 index 00000000000..fff1742c27d --- /dev/null +++ b/idea/idea-completion/testData/basic/common/annotations/Deprecated.kt @@ -0,0 +1,6 @@ +@Dep +fun foo() { } + +// INVOCATION_COUNT: 1 +// EXIST: { itemText: "Deprecated", tailText: " (kotlin)" } +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/basic/common/annotations/Deprecated2.kt b/idea/idea-completion/testData/basic/common/annotations/Deprecated2.kt new file mode 100644 index 00000000000..353cd16bd12 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/annotations/Deprecated2.kt @@ -0,0 +1,8 @@ +@Dep +fun foo() { } + +// INVOCATION_COUNT: 2 +// WITH_ORDER +// EXIST: { itemText: "Deprecated", tailText: " (kotlin)" } +// EXIST_JAVA_ONLY: { itemText: "Deprecated", tailText: " (java.lang)" } +// NOTHING_ELSE diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index d108d38410f..788acd1b483 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -735,6 +735,18 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes doTest(fileName); } + @TestMetadata("Deprecated.kt") + public void testDeprecated() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/Deprecated.kt"); + doTest(fileName); + } + + @TestMetadata("Deprecated2.kt") + public void testDeprecated2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/Deprecated2.kt"); + doTest(fileName); + } + @TestMetadata("FunctionAnnotation1.kt") public void testFunctionAnnotation1() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/FunctionAnnotation1.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index 6920f2ea690..9d8c21a0282 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -735,6 +735,18 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("Deprecated.kt") + public void testDeprecated() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/Deprecated.kt"); + doTest(fileName); + } + + @TestMetadata("Deprecated2.kt") + public void testDeprecated2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/Deprecated2.kt"); + doTest(fileName); + } + @TestMetadata("FunctionAnnotation1.kt") public void testFunctionAnnotation1() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/annotations/FunctionAnnotation1.kt"); diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ImportableFqNameClassifier.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ImportableFqNameClassifier.kt index 139efd0c0b3..822a5146a90 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ImportableFqNameClassifier.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ImportableFqNameClassifier.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.idea.core import org.jetbrains.kotlin.idea.util.ImportInsertHelper +import org.jetbrains.kotlin.load.java.components.JavaAnnotationMapper import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.psi.JetFile @@ -65,12 +66,19 @@ class ImportableFqNameClassifier(private val file: JetFile) { } return when { - JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName).isNotEmpty() -> Classification.notToBeUsedInKotlin + JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName).isNotEmpty() + || JavaAnnotationMapper.javaToKotlinNameMap[fqName] != null -> Classification.notToBeUsedInKotlin + fqName.parent() == file.packageFqName -> Classification.fromCurrentPackage + ImportInsertHelper.getInstance(file.project).isImportedWithDefault(importPath, file) -> Classification.defaultImport + isImportedWithPreciseImport(fqName) -> Classification.preciseImport + isImportedWithAllUnderImport(fqName) -> Classification.allUnderImport + hasPreciseImportFromPackage(fqName.parent()) -> Classification.hasImportFromSamePackage + else -> Classification.notImported } }