Completion to complete non-imported classes on the first invocation
#KT-8527 Fixed
This commit is contained in:
+11
-2
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+22
-12
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<FuzzyType> {
|
||||
val provider = CollectRequiredTypesContextVariablesProvider()
|
||||
val lookupElementFactory = createLookupElementFactory(provider)
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+12
-7
@@ -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<LookupElement>()
|
||||
|
||||
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<String>) {
|
||||
resultSet.restartCompletionOnPrefixChange(prefixCondition)
|
||||
}
|
||||
|
||||
+1
-1
@@ -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) }
|
||||
|
||||
@@ -8,4 +8,3 @@ class Test {
|
||||
// EXIST: StringBuilder
|
||||
// EXIST_JAVA_ONLY: StringBuffer
|
||||
// ABSENT: HTMLStyleElement
|
||||
// ABSENT: Statement
|
||||
|
||||
@@ -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
|
||||
// EXIST: StrInner
|
||||
@@ -12,4 +12,3 @@ class SomeClass {
|
||||
// EXIST: StringBuilder
|
||||
// EXIST_JAVA_ONLY: StringBuffer
|
||||
// ABSENT: HTMLStyleElement
|
||||
// ABSENT: Statement
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
class Xyz
|
||||
class Xyzzz
|
||||
|
||||
fun foo(xyz: Xyz){}
|
||||
fun foo(xyz: Xyzzzz){}
|
||||
|
||||
fun bar(o: Any) {
|
||||
foo(o as Xy<caret>)
|
||||
foo(o as Xyz<caret>)
|
||||
}
|
||||
|
||||
// EXIST: Xyz
|
||||
// EXIST: Xyzzz
|
||||
// NOTHING_ELSE
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class Host
|
||||
|
||||
fun foo(): Ho<caret>
|
||||
|
||||
// INVOCATION_COUNT: 1
|
||||
// EXIST: Host
|
||||
// ABSENT: UnknownHostException
|
||||
@@ -0,0 +1,7 @@
|
||||
class Host
|
||||
|
||||
fun foo(): Ho<caret>
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// EXIST: Host
|
||||
// EXIST: UnknownHostException
|
||||
@@ -1,8 +1,7 @@
|
||||
import java.util.HashMap
|
||||
|
||||
fun foo() {
|
||||
val v = HashMap<String, <caret>
|
||||
val v = HashMap<String, H<caret>
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// ELEMENT: HashSet
|
||||
@@ -5,5 +5,4 @@ fun foo() {
|
||||
val v = HashMap<String, HashSet<caret>
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 2
|
||||
// ELEMENT: HashSet
|
||||
+12
@@ -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");
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
fun isJavaClassNotToBeUsedInKotlin(fqName: FqName): Boolean
|
||||
= JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName, DefaultBuiltIns.Instance).isNotEmpty() || JavaAnnotationMapper.javaToKotlinNameMap[fqName] != null
|
||||
Reference in New Issue
Block a user