KT-13266 Non-imported type aliases do not appear in completion

#KT-13266 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-08-31 20:15:57 +03:00
parent fcce1e3838
commit 50a0f9a4a7
14 changed files with 124 additions and 40 deletions
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiLiteral
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters
import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper
import org.jetbrains.kotlin.idea.core.isJavaClassNotToBeUsedInKotlin
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
@@ -41,23 +42,30 @@ class AllClassesCompletion(private val parameters: CompletionParameters,
private val prefixMatcher: PrefixMatcher,
private val resolutionFacade: ResolutionFacade,
private val kindFilter: (ClassKind) -> Boolean,
private val includeTypeAliases: Boolean,
private val includeJavaClassesNotToBeUsed: Boolean
) {
fun collect(classDescriptorCollector: (ClassDescriptor) -> Unit, javaClassCollector: (PsiClass) -> Unit) {
fun collect(classifierDescriptorCollector: (ClassifierDescriptorWithTypeParameters) -> Unit, javaClassCollector: (PsiClass) -> Unit) {
//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
for (builtinPackage in resolutionFacade.moduleDescriptor.builtIns.builtInsPackageFragments) {
collectClassesFromScope(builtinPackage.getMemberScope()) {
if (it.containingDeclaration is ClassDescriptor) {
classDescriptorCollector(it)
classifierDescriptorCollector(it)
}
}
}
kotlinIndicesHelper
.getKotlinClasses({ prefixMatcher.prefixMatches(it) }, kindFilter)
.forEach { classDescriptorCollector(it) }
.forEach { classifierDescriptorCollector(it) }
if (includeTypeAliases) {
kotlinIndicesHelper
.getTopLevelTypeAliases(prefixMatcher.asStringNameFilter())
.forEach { classifierDescriptorCollector(it) }
}
if (!ProjectStructureUtil.isJsKotlinModule(parameters.originalFile as KtFile)) {
addAdaptedJavaCompletion(javaClassCollector)
@@ -28,6 +28,7 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.ProcessingContext
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters
import org.jetbrains.kotlin.idea.completion.handlers.createKeywordConstructLookupElement
import org.jetbrains.kotlin.idea.completion.smart.ExpectedInfoMatch
import org.jetbrains.kotlin.idea.completion.smart.SMART_COMPLETION_ITEM_PRIORITY_KEY
@@ -291,17 +292,30 @@ class BasicCompletionSession(
if (callTypeAndReceiver.receiver == null && prefix.isNotEmpty()) {
val classKindFilter: ((ClassKind) -> Boolean)?
val includeTypeAliases: Boolean
when (callTypeAndReceiver) {
is CallTypeAndReceiver.ANNOTATION -> classKindFilter = { it == ClassKind.ANNOTATION_CLASS }
is CallTypeAndReceiver.DEFAULT, is CallTypeAndReceiver.TYPE -> classKindFilter = { it != ClassKind.ENUM_ENTRY }
else -> classKindFilter = null
is CallTypeAndReceiver.ANNOTATION -> {
classKindFilter = { it == ClassKind.ANNOTATION_CLASS }
includeTypeAliases = false
}
is CallTypeAndReceiver.DEFAULT, is CallTypeAndReceiver.TYPE -> {
classKindFilter = { it != ClassKind.ENUM_ENTRY }
includeTypeAliases = true
}
else -> {
classKindFilter = null
includeTypeAliases = false
}
}
if (classKindFilter != null) {
val prefixMatcher = if (configuration.useBetterPrefixMatcherForNonImportedClasses)
BetterPrefixMatcher(prefixMatcher, collector.bestMatchingDegree)
else
prefixMatcher
addClassesFromIndex(classKindFilter, prefixMatcher)
addClassesFromIndex(classKindFilter, includeTypeAliases, prefixMatcher)
}
}
}
@@ -572,15 +586,16 @@ class BasicCompletionSession(
}
}
private fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean, prefixMatcher: PrefixMatcher) {
val classDescriptorCollector = { descriptor: ClassDescriptor ->
private fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean, includeTypeAliases: Boolean, prefixMatcher: PrefixMatcher) {
val classifierDescriptorCollector = { descriptor: ClassifierDescriptorWithTypeParameters ->
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)
AllClassesCompletion(parameters, indicesHelper(true), prefixMatcher, resolutionFacade,
kindFilter, includeTypeAliases, configuration.completeJavaClassesNotToBeUsed
).collect(classifierDescriptorCollector, javaClassCollector)
}
}
@@ -89,7 +89,8 @@ class ParameterNameAndTypeCompletion(
fun addFromAllClasses(parameters: CompletionParameters, indicesHelper: KotlinIndicesHelper) {
for ((classNameMatcher, userPrefix) in classNamePrefixMatchers.zip(userPrefixes)) {
AllClassesCompletion(
parameters, indicesHelper, classNameMatcher, resolutionFacade, { !it.isSingleton }, includeJavaClassesNotToBeUsed = false
parameters, indicesHelper, classNameMatcher, resolutionFacade, { !it.isSingleton },
includeTypeAliases = true, includeJavaClassesNotToBeUsed = false
).collect(
{ addSuggestionsForClassifier(it, userPrefix, notImported = true) },
{ addSuggestionsForJavaClass(it, userPrefix, notImported = true) }
@@ -0,0 +1,4 @@
package test
class MyClass
typealias MyAlias = MyClass
@@ -0,0 +1,5 @@
package test2
fun foo(ali<caret>)
// EXIST: { lookupString: "alias : MyAlias", itemText: "alias: MyAlias", typeText: "public typealias MyAlias = MyClass defined in test", attributes: "" }
@@ -0,0 +1,4 @@
package dependency
class MyClass
typealias MyAlias = MyClass
@@ -0,0 +1,10 @@
package test
typealias MySameFileAlias = (String) -> Int
private typealias MyPrivateAlias = (String, Char) -> Unit
val test: My<caret>
// EXIST: { lookupString: "MySameFileAlias", itemText: "MySameFileAlias", tailText: null, typeText: "public typealias MySameFileAlias = (String) -> Int defined in test", attributes: "" }
// EXIST: { lookupString: "MyPrivateAlias", itemText: "MyPrivateAlias", tailText: null, typeText: "private typealias MyPrivateAlias = (String, Char) -> Unit defined in test", attributes: "" }
// EXIST: { lookupString: "MyAlias", itemText: "MyAlias", tailText: null, typeText: "public typealias MyAlias = MyClass defined in dependency", attributes: "" }
@@ -0,0 +1 @@
fun foo(): MyAl<caret>
@@ -0,0 +1,5 @@
package test
class MyClass
typealias MyAlias = MyClass
@@ -0,0 +1,3 @@
import test.MyAlias
fun foo(): MyAlias<caret>
@@ -323,6 +323,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
doTest(fileName);
}
@TestMetadata("ParameterNameAndTypeForNotImportedAlias")
public void testParameterNameAndTypeForNotImportedAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeForNotImportedAlias/");
doTest(fileName);
}
@TestMetadata("ParameterNameAndTypeNestedClasses")
public void testParameterNameAndTypeNestedClasses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeNestedClasses/");
@@ -400,4 +406,10 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/TopLevelFunction/");
doTest(fileName);
}
@TestMetadata("TypeAliases")
public void testTypeAliases() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/TypeAliases/");
doTest(fileName);
}
}
@@ -101,6 +101,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() {
doTest()
}
fun testNotImportedTypeAlias() {
doTest()
}
fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) {
val fileName = getTestName(false)