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 af2d24b219c..15f21a229ff 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 @@ -25,7 +25,9 @@ import org.jetbrains.kotlin.asJava.KtLightClass import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper +import org.jetbrains.kotlin.idea.core.isJavaClassNotToBeUsedInKotlin import org.jetbrains.kotlin.idea.project.ProjectStructureUtil +import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader @@ -38,7 +40,8 @@ class AllClassesCompletion(private val parameters: CompletionParameters, private val kotlinIndicesHelper: KotlinIndicesHelper, private val prefixMatcher: PrefixMatcher, private val resolutionFacade: ResolutionFacade, - private val kindFilter: (ClassKind) -> Boolean + private val kindFilter: (ClassKind) -> Boolean, + private val includeJavaClassesNotToBeUsed: Boolean ) { fun collect(classDescriptorCollector: (ClassDescriptor) -> Unit, javaClassCollector: (PsiClass) -> Unit) { @@ -84,7 +87,7 @@ class AllClassesCompletion(private val parameters: CompletionParameters, psiClass.isEnum -> ClassKind.ENUM_CLASS else -> ClassKind.CLASS } - if (kindFilter(kind)) { + if (kindFilter(kind) && !isNotToBeUsed(psiClass)) { collector(psiClass) } } @@ -97,4 +100,10 @@ class AllClassesCompletion(private val parameters: CompletionParameters, return (metadata?.findAttributeValue(JvmAnnotationNames.KIND_FIELD_NAME) as? PsiLiteral)?.value == KotlinClassHeader.Kind.SYNTHETIC_CLASS.id } + + private fun isNotToBeUsed(javaClass: PsiClass): Boolean { + if (includeJavaClassesNotToBeUsed) return false + val fqName = javaClass.getKotlinFqName() + return fqName != null && isJavaClassNotToBeUsedInKotlin(fqName) + } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt index 8c6f3a94ccc..8ff8755724d 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt @@ -16,16 +16,15 @@ package org.jetbrains.kotlin.idea.completion -import com.intellij.codeInsight.completion.CompletionParameters -import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.codeInsight.completion.CompletionSorter -import com.intellij.codeInsight.completion.CompletionType +import com.intellij.codeInsight.completion.* +import com.intellij.codeInsight.completion.impl.BetterPrefixMatcher import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.codeInsight.template.TemplateManager import com.intellij.patterns.PatternCondition import com.intellij.patterns.StandardPatterns import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiClass import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.ProcessingContext import org.jetbrains.kotlin.descriptors.ClassDescriptor @@ -210,7 +209,7 @@ class BasicCompletionSession( packageNames.forEach { collector.addElement(basicLookupElementFactory.createLookupElementForPackage(it)) } } - + flushToResultSet() NamedArgumentCompletion.complete(collector, expectedInfos, callTypeAndReceiver.callType) @@ -271,7 +270,7 @@ class BasicCompletionSession( } } - if (callTypeAndReceiver.receiver == null) { + if (callTypeAndReceiver.receiver == null && prefix.isNotEmpty()) { val classKindFilter: ((ClassKind) -> Boolean)? when (callTypeAndReceiver) { is CallTypeAndReceiver.ANNOTATION -> classKindFilter = { it == ClassKind.ANNOTATION_CLASS } @@ -279,12 +278,11 @@ class BasicCompletionSession( else -> classKindFilter = null } if (classKindFilter != null) { - if (configuration.completeNonImportedClasses) { - addClassesFromIndex(classKindFilter) - } - else { - collector.advertiseSecondCompletion() - } + val prefixMatcher = if (configuration.useBetterPrefixMatcherForNonImportedClasses) + BetterPrefixMatcher(prefixMatcher, collector.bestMatchingDegree) + else + prefixMatcher + addClassesFromIndex(classKindFilter, prefixMatcher) } } } @@ -549,4 +547,16 @@ class BasicCompletionSession( else -> return null } } + + private fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean, prefixMatcher: PrefixMatcher) { + val classDescriptorCollector = { descriptor: ClassDescriptor -> + collector.addElement(basicLookupElementFactory.createLookupElement(descriptor), notImported = true) + } + val javaClassCollector = { javaClass: PsiClass -> + collector.addElement(basicLookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) + } + AllClassesCompletion(parameters, indicesHelper(true), prefixMatcher, resolutionFacade, kindFilter, configuration.completeJavaClassesNotToBeUsed) + .collect(classDescriptorCollector, javaClassCollector) + } + } \ No newline at end of file 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 d73caab4dac..695b8cbbf5b 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 @@ -50,7 +50,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import java.util.* class CompletionSessionConfiguration( - val completeNonImportedClasses: Boolean, + val useBetterPrefixMatcherForNonImportedClasses: Boolean, val completeNonAccessibleDeclarations: Boolean, val filterOutJavaGettersAndSetters: Boolean, val completeJavaClassesNotToBeUsed: Boolean, @@ -58,7 +58,7 @@ class CompletionSessionConfiguration( ) fun CompletionSessionConfiguration(parameters: CompletionParameters) = CompletionSessionConfiguration( - completeNonImportedClasses = parameters.invocationCount >= 2, + useBetterPrefixMatcherForNonImportedClasses = parameters.invocationCount < 2, completeNonAccessibleDeclarations = parameters.invocationCount >= 2, filterOutJavaGettersAndSetters = parameters.invocationCount < 2, completeJavaClassesNotToBeUsed = parameters.invocationCount >= 2, @@ -181,8 +181,7 @@ abstract class CompletionSession( private fun isVisibleDescriptor(descriptor: DeclarationDescriptor, completeNonAccessible: Boolean): 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.importableFqName?.let { isJavaClassNotToBeUsedInKotlin(it) } == true) return false } if (descriptor is TypeParameterDescriptor && !isTypeParameterVisible(descriptor)) return false @@ -413,14 +412,6 @@ abstract class CompletionSession( && this !is CallTypeAndReceiver.IMPORT_DIRECTIVE } - protected fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean) { - AllClassesCompletion(parameters, indicesHelper(true), prefixMatcher, resolutionFacade, kindFilter) - .collect( - { descriptor -> collector.addElement(basicLookupElementFactory.createLookupElement(descriptor), notImported = true) }, - { javaClass -> collector.addElement(basicLookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) } - ) - } - protected fun withCollectRequiredContextVariableTypes(action: (LookupElementFactory) -> Unit): Collection { val provider = CollectRequiredTypesContextVariablesProvider() val lookupElementFactory = createLookupElementFactory(provider) 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 34e62d27bec..288ff0f90ff 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 @@ -309,7 +309,7 @@ class KotlinCompletionContributor : CompletionContributor() { if (!somethingAdded && parameters.invocationCount < 2) { // Rerun completion if nothing was found val newConfiguration = CompletionSessionConfiguration( - completeNonImportedClasses = true, + useBetterPrefixMatcherForNonImportedClasses = false, completeNonAccessibleDeclarations = false, filterOutJavaGettersAndSetters = false, completeJavaClassesNotToBeUsed = false, diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt index 2907e654af0..8c4a99c23d9 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.idea.completion import com.intellij.codeInsight.completion.* +import com.intellij.codeInsight.completion.impl.RealPrefixMatchingWeigher import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.openapi.util.TextRange @@ -34,6 +35,9 @@ class LookupElementsCollector( sorter: CompletionSorter ) { + var bestMatchingDegree = Int.MIN_VALUE + private set + private val elements = ArrayList() private val resultSet = resultSet @@ -42,17 +46,22 @@ class LookupElementsCollector( private val postProcessors = ArrayList<(LookupElement) -> LookupElement>() + var isResultEmpty: Boolean = true + private set + fun flushToResultSet() { if (!elements.isEmpty()) { resultSet.addAllElements(elements) elements.clear() isResultEmpty = false + + for (element in elements) { + val matchingDegree = RealPrefixMatchingWeigher.getBestMatchingDegree(element, prefixMatcher) + bestMatchingDegree = Math.max(bestMatchingDegree, matchingDegree) + } } } - var isResultEmpty: Boolean = true - private set - fun addLookupElementPostProcessor(processor: (LookupElement) -> LookupElement) { postProcessors.add(processor) } @@ -142,10 +151,6 @@ class LookupElementsCollector( elements.forEach { addElement(it, notImported) } } - fun advertiseSecondCompletion() { - JavaCompletionContributor.advertiseSecondCompletion(completionParameters.originalFile.project, resultSet) - } - fun restartCompletionOnPrefixChange(prefixCondition: ElementPattern) { resultSet.restartCompletionOnPrefixChange(prefixCondition) } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt index c58034dcddc..9227a68c032 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt @@ -89,7 +89,7 @@ class ParameterNameAndTypeCompletion( fun addFromAllClasses(parameters: CompletionParameters, indicesHelper: KotlinIndicesHelper) { for ((classNameMatcher, userPrefix) in classNamePrefixMatchers.zip(userPrefixes)) { AllClassesCompletion( - parameters, indicesHelper, classNameMatcher, resolutionFacade, { !it.isSingleton } + parameters, indicesHelper, classNameMatcher, resolutionFacade, { !it.isSingleton }, includeJavaClassesNotToBeUsed = false ).collect( { addSuggestionsForClassifier(it, userPrefix, notImported = true) }, { addSuggestionsForJavaClass(it, userPrefix, notImported = true) } diff --git a/idea/idea-completion/testData/basic/common/ExtensionForProperty.kt b/idea/idea-completion/testData/basic/common/ExtensionForProperty.kt index b5db1fa5f60..6d197b1abf1 100644 --- a/idea/idea-completion/testData/basic/common/ExtensionForProperty.kt +++ b/idea/idea-completion/testData/basic/common/ExtensionForProperty.kt @@ -8,4 +8,3 @@ class Test { // EXIST: StringBuilder // EXIST_JAVA_ONLY: StringBuffer // ABSENT: HTMLStyleElement -// ABSENT: Statement diff --git a/idea/idea-completion/testData/basic/common/ExtensionInsideFunction.kt b/idea/idea-completion/testData/basic/common/ExtensionInsideFunction.kt index dc25c9c80bb..bd39fef4d1e 100644 --- a/idea/idea-completion/testData/basic/common/ExtensionInsideFunction.kt +++ b/idea/idea-completion/testData/basic/common/ExtensionInsideFunction.kt @@ -9,7 +9,7 @@ class StrSome { } class StrMore { - class StrAbsent + class StrInner } // INVOCATION_COUNT: 1 @@ -19,4 +19,4 @@ class StrMore { // EXIST: StrInFun // EXIST: StringBuilder // EXIST_JAVA_ONLY: StringBuffer -// ABSENT: StrAbsent \ No newline at end of file +// EXIST: StrInner \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/InParametersTypes.kt b/idea/idea-completion/testData/basic/common/InParametersTypes.kt index c3535a9797f..05430f52ef4 100644 --- a/idea/idea-completion/testData/basic/common/InParametersTypes.kt +++ b/idea/idea-completion/testData/basic/common/InParametersTypes.kt @@ -12,4 +12,3 @@ class SomeClass { // EXIST: StringBuilder // EXIST_JAVA_ONLY: StringBuffer // ABSENT: HTMLStyleElement -// ABSENT: Statement \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/fromSmart/AfterAsNoDuplicates.kt b/idea/idea-completion/testData/basic/common/fromSmart/AfterAsNoDuplicates.kt index b4bd29c3dee..4cd409b7823 100644 --- a/idea/idea-completion/testData/basic/common/fromSmart/AfterAsNoDuplicates.kt +++ b/idea/idea-completion/testData/basic/common/fromSmart/AfterAsNoDuplicates.kt @@ -1,10 +1,10 @@ -class Xyz +class Xyzzz -fun foo(xyz: Xyz){} +fun foo(xyz: Xyzzzz){} fun bar(o: Any) { - foo(o as Xy) + foo(o as Xyz) } -// EXIST: Xyz +// EXIST: Xyzzz // NOTHING_ELSE diff --git a/idea/idea-completion/testData/basic/java/NonImportedPrefixMatching1.kt b/idea/idea-completion/testData/basic/java/NonImportedPrefixMatching1.kt new file mode 100644 index 00000000000..5e0e8593cb0 --- /dev/null +++ b/idea/idea-completion/testData/basic/java/NonImportedPrefixMatching1.kt @@ -0,0 +1,7 @@ +class Host + +fun foo(): Ho + +// INVOCATION_COUNT: 1 +// EXIST: Host +// ABSENT: UnknownHostException \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/java/NonImportedPrefixMatching2.kt b/idea/idea-completion/testData/basic/java/NonImportedPrefixMatching2.kt new file mode 100644 index 00000000000..394d2b4397e --- /dev/null +++ b/idea/idea-completion/testData/basic/java/NonImportedPrefixMatching2.kt @@ -0,0 +1,7 @@ +class Host + +fun foo(): Ho + +// INVOCATION_COUNT: 2 +// EXIST: Host +// EXIST: UnknownHostException \ No newline at end of file diff --git a/idea/idea-completion/testData/handlers/basic/SecondTypeArg.kt b/idea/idea-completion/testData/handlers/basic/SecondTypeArg.kt index 40a02b884a7..7f0d77b79e8 100644 --- a/idea/idea-completion/testData/handlers/basic/SecondTypeArg.kt +++ b/idea/idea-completion/testData/handlers/basic/SecondTypeArg.kt @@ -1,8 +1,7 @@ import java.util.HashMap fun foo() { - val v = HashMap + val v = HashMap } -// INVOCATION_COUNT: 2 // ELEMENT: HashSet \ No newline at end of file diff --git a/idea/idea-completion/testData/handlers/basic/SecondTypeArg.kt.after b/idea/idea-completion/testData/handlers/basic/SecondTypeArg.kt.after index b1a375b1f3f..a77f40ce2a7 100644 --- a/idea/idea-completion/testData/handlers/basic/SecondTypeArg.kt.after +++ b/idea/idea-completion/testData/handlers/basic/SecondTypeArg.kt.after @@ -5,5 +5,4 @@ fun foo() { val v = HashMap } -// INVOCATION_COUNT: 2 // ELEMENT: HashSet \ No newline at end of file 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 6fe6d067dca..e263a8325cb 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 @@ -2632,6 +2632,18 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("NonImportedPrefixMatching1.kt") + public void testNonImportedPrefixMatching1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/NonImportedPrefixMatching1.kt"); + doTest(fileName); + } + + @TestMetadata("NonImportedPrefixMatching2.kt") + public void testNonImportedPrefixMatching2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/NonImportedPrefixMatching2.kt"); + doTest(fileName); + } + @TestMetadata("Number.kt") public void testNumber() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/Number.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 37140b793e8..772b89a036d 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 @@ -67,8 +67,7 @@ class ImportableFqNameClassifier(private val file: KtFile) { } return when { - JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName, DefaultBuiltIns.Instance).isNotEmpty() - || JavaAnnotationMapper.javaToKotlinNameMap[fqName] != null -> Classification.notToBeUsedInKotlin + isJavaClassNotToBeUsedInKotlin(fqName) -> Classification.notToBeUsedInKotlin fqName.parent() == file.packageFqName -> Classification.fromCurrentPackage @@ -87,4 +86,7 @@ class ImportableFqNameClassifier(private val file: KtFile) { private fun isImportedWithPreciseImport(name: FqName) = name in preciseImports private fun isImportedWithAllUnderImport(name: FqName) = name.parent() in allUnderImports private fun hasPreciseImportFromPackage(packageName: FqName) = packageName in preciseImportPackages -} \ No newline at end of file +} + +fun isJavaClassNotToBeUsedInKotlin(fqName: FqName): Boolean + = JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName, DefaultBuiltIns.Instance).isNotEmpty() || JavaAnnotationMapper.javaToKotlinNameMap[fqName] != null \ No newline at end of file