Code completion: avoid duplication of elements for java classes

This commit is contained in:
Valentin Kipyatkov
2014-10-08 18:09:16 +04:00
parent eabcbb00ab
commit f9bf214526
8 changed files with 64 additions and 54 deletions
@@ -34,6 +34,11 @@ import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler
import org.jetbrains.jet.renderer.DescriptorRenderer
import org.jetbrains.jet.plugin.completion.handlers.BaseDeclarationInsertHandler
import org.jetbrains.jet.plugin.completion.handlers.JetPropertyInsertHandler
import com.intellij.psi.PsiClass
import org.jetbrains.jet.asJava.KotlinLightClass
import org.jetbrains.jet.lang.resolve.java.JavaResolverPsiUtils
import org.jetbrains.jet.plugin.completion.handlers.JetJavaClassInsertHandler
import com.intellij.codeInsight.completion.JavaPsiClassReferenceElement
public object DescriptorLookupConverter {
public fun createLookupElement(analyzer: KotlinCodeAnalyzer, descriptor: DeclarationDescriptor): LookupElement {
@@ -44,7 +49,21 @@ public object DescriptorLookupConverter {
return createLookupElement(analyzer, _descriptor, DescriptorToSourceUtils.descriptorToDeclaration(_descriptor))
}
public fun createLookupElementForJavaClass(psiClass: PsiClass): LookupElement {
return JavaPsiClassReferenceElement(psiClass).setInsertHandler(JetJavaClassInsertHandler)
}
private fun createLookupElement(analyzer: KotlinCodeAnalyzer, descriptor: DeclarationDescriptor, declaration: PsiElement?): LookupElement {
if (descriptor is ClassifierDescriptor &&
declaration is PsiClass &&
declaration !is KotlinLightClass &&
!JavaResolverPsiUtils.isCompiledKotlinClass(declaration)) {
// for java classes we create special lookup elements
// because they must be equal to ones created in TypesCompletion
// otherwise we may have duplicates
return createLookupElementForJavaClass(declaration)
}
val name = descriptor.getName().asString()
var element = LookupElementBuilder.create(DeclarationLookupObject(descriptor, analyzer, declaration), name)
@@ -21,13 +21,13 @@ import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.plugin.completion.handlers.*
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
import com.intellij.codeInsight.completion.PrefixMatcher
import java.util.ArrayList
import com.intellij.codeInsight.completion.CompletionResultSet
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
class LookupElementsCollector(private val prefixMatcher: PrefixMatcher,
private val resolveSession: ResolveSessionForBodies,
@@ -57,12 +57,13 @@ class LookupElementsCollector(private val prefixMatcher: PrefixMatcher,
if (!descriptorFilter(descriptor)) return
run {
var lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, descriptor)
if (suppressAutoInsertion &&
elements.isEmpty() && isResultEmpty /* without these checks we would get duplicated items */) {
lookupElement = lookupElement.suppressAutoInsertion()
val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, descriptor)
if (suppressAutoInsertion) {
addElementWithAutoInsertionSuppressed(lookupElement)
}
else {
addElement(lookupElement)
}
addElement(lookupElement)
}
// add special item for function with one argument of function type with more than one parameter
@@ -96,6 +97,15 @@ class LookupElementsCollector(private val prefixMatcher: PrefixMatcher,
}
}
public fun addElementWithAutoInsertionSuppressed(element: LookupElement) {
if (isResultEmpty && elements.isEmpty()) { /* without these checks we may get duplicated items */
addElement(element.suppressAutoInsertion())
}
else {
addElement(element)
}
}
public fun addElements(elements: Iterable<LookupElement>) {
elements.forEach { addElement(it) }
}
@@ -17,10 +17,7 @@
package org.jetbrains.jet.plugin.completion
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.psi.PsiClass
import com.intellij.util.Consumer
import org.jetbrains.jet.asJava.KotlinLightClass
import org.jetbrains.jet.lang.descriptors.ClassKind
import org.jetbrains.jet.lang.psi.JetFile
@@ -29,7 +26,6 @@ import org.jetbrains.jet.lang.resolve.lazy.ResolveSessionUtils
import org.jetbrains.jet.lang.resolve.name.FqName
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.plugin.caches.JetFromJavaDescriptorHelper
import org.jetbrains.jet.plugin.completion.handlers.JetJavaClassInsertHandler
import org.jetbrains.jet.plugin.project.ProjectStructureUtil
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
import org.jetbrains.jet.plugin.caches.KotlinIndicesHelper
@@ -53,42 +49,26 @@ class TypesCompletion(val parameters: CompletionParameters, val resolveSession:
/**
* Add java elements with performing conversion to kotlin elements if necessary.
*/
private fun addAdaptedJavaCompletion(result: LookupElementsCollector) {
JavaClassNameCompletionContributor.addAllClasses(parameters, true, prefixMatcher, object : Consumer<LookupElement> {
override fun consume(lookupElement: LookupElement?) {
if (lookupElement is JavaPsiClassReferenceElement) {
val psiClass = lookupElement.getObject()
if (addJavaClassAsJetLookupElement(psiClass, result)) return
result.addElement(object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun handleInsert(context: InsertionContext) {
JetJavaClassInsertHandler.handleInsert(context, lookupElement)
}
}.suppressAutoInsertion())
private fun addAdaptedJavaCompletion(collector: LookupElementsCollector) {
AllClassesGetter.processJavaClasses(parameters, prefixMatcher, true, { psiClass ->
if (psiClass!! !is KotlinLightClass) { // Kotlin non-compiled class should have already been added as kotlin element before
if (JavaResolverPsiUtils.isCompiledKotlinClass(psiClass)) {
addLookupElementForCompiledKotlinClass(psiClass, collector)
}
else {
collector.addElementWithAutoInsertionSuppressed(DescriptorLookupConverter.createLookupElementForJavaClass(psiClass))
}
}
})
}
private fun addJavaClassAsJetLookupElement(aClass: PsiClass, result: LookupElementsCollector): Boolean {
if (aClass is KotlinLightClass) {
// Do nothing. Kotlin not-compiled class should have already been added as kotlin element before.
return true
}
if (JavaResolverPsiUtils.isCompiledKotlinClass(aClass)) {
if (JetFromJavaDescriptorHelper.getCompiledClassKind(aClass) != ClassKind.CLASS_OBJECT) {
val qualifiedName = aClass.getQualifiedName()
if (qualifiedName != null) {
result.addDescriptorElements(ResolveSessionUtils.getClassDescriptorsByFqName(resolveSession.getModuleDescriptor(), FqName(qualifiedName)),
suppressAutoInsertion = true)
}
private fun addLookupElementForCompiledKotlinClass(aClass: PsiClass, collector: LookupElementsCollector) {
if (JetFromJavaDescriptorHelper.getCompiledClassKind(aClass) != ClassKind.CLASS_OBJECT) {
val qualifiedName = aClass.getQualifiedName()
if (qualifiedName != null) {
val descriptors = ResolveSessionUtils.getClassDescriptorsByFqName(resolveSession.getModuleDescriptor(), FqName(qualifiedName))
collector.addDescriptorElements(descriptors, suppressAutoInsertion = true)
}
return true
}
return false
}
}
@@ -28,7 +28,7 @@ import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper
* Handler for inserting java class completion.
* - Should place import directive if necessary.
*/
public object JetJavaClassInsertHandler : InsertHandler<JavaPsiClassReferenceElement> {
object JetJavaClassInsertHandler : InsertHandler<JavaPsiClassReferenceElement> {
override fun handleInsert(context: InsertionContext, item: JavaPsiClassReferenceElement) {
PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments()
@@ -2,6 +2,6 @@ import java.io.InputStreamReader
val x = InputStreamReader<caret>
// INVOCATION_COUNT: 1
// INVOCATION_COUNT: 2
// EXIST: { lookupString:"InputStreamReader", tailText:" (java.io)" }
// NUMBER: 1
@@ -17,13 +17,10 @@
package org.jetbrains.jet.completion;
import com.intellij.testFramework.TestDataPath;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.junit.runner.RunWith;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata;
import org.jetbrains.jet.JUnit3RunnerWithInners;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
@@ -846,15 +843,15 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("NoClassNameDuplicationForRuntimeClass.kt")
public void testNoClassNameDuplicationForRuntimeClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/java/NoClassNameDuplicationForRuntimeClass.kt");
@TestMetadata("NoDuplicationForImportedJavaClass.kt")
public void testNoDuplicationForImportedJavaClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/java/NoDuplicationForImportedJavaClass.kt");
doTest(fileName);
}
@TestMetadata("NoImportedJavaClassDuplication.kt")
public void testNoImportedJavaClassDuplication() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/java/NoImportedJavaClassDuplication.kt");
@TestMetadata("NoDuplicationForRuntimeClass.kt")
public void testNoDuplicationForRuntimeClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/java/NoDuplicationForRuntimeClass.kt");
doTest(fileName);
}
@@ -17,9 +17,13 @@
package org.jetbrains.jet.completion;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.TestMetadata;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.junit.runner.RunWith;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata;
import org.jetbrains.jet.JUnit3RunnerWithInners;
import java.io.File;
import java.util.regex.Pattern;