Smart completion: java static members, enum members and class object members added

This commit is contained in:
Valentin Kipyatkov
2013-12-12 22:32:53 +04:00
parent dbaaa80918
commit db77eb54d7
23 changed files with 263 additions and 3 deletions
@@ -26,14 +26,26 @@
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder setTypeText(java.lang.String, boolean)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withIcon(javax.swing.Icon)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withInsertHandler(com.intellij.codeInsight.completion.InsertHandler&lt;com.intellij.codeInsight.lookup.LookupElement&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withLookupString(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withPresentableText(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withStrikeoutness(boolean)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withTailText(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
@@ -30,7 +30,7 @@ import java.util.List;
public interface ClassDescriptor extends ClassifierDescriptor, MemberDescriptor, ClassOrNamespaceDescriptor {
@NotNull
JetScope getMemberScope(List<? extends TypeProjection> typeArguments);
JetScope getMemberScope(@NotNull List<? extends TypeProjection> typeArguments);
@NotNull
JetScope getUnsubstitutedInnerClassesScope();
@@ -107,7 +107,7 @@ public abstract class AbstractClassDescriptor implements ClassDescriptor {
@NotNull
@Override
public JetScope getMemberScope(List<? extends TypeProjection> typeArguments) {
public JetScope getMemberScope(@NotNull List<? extends TypeProjection> typeArguments) {
assert typeArguments.size() == getTypeConstructor().getParameters().size() : "Illegal number of type arguments: expected "
+ getTypeConstructor().getParameters().size() + " but was " + typeArguments.size()
+ " for " + getTypeConstructor() + " " + getTypeConstructor().getParameters();
@@ -87,7 +87,7 @@ public class LazySubstitutingClassDescriptor implements ClassDescriptor {
@NotNull
@Override
public JetScope getMemberScope(List<? extends TypeProjection> typeArguments) {
public JetScope getMemberScope(@NotNull List<? extends TypeProjection> typeArguments) {
JetScope memberScope = original.getMemberScope(typeArguments);
if (originalSubstitutor.isEmpty()) {
return memberScope;
@@ -19,6 +19,12 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration
import org.jetbrains.jet.lang.resolve.name.Name
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPropertyDescriptor
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaClassDescriptor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiModifier
import com.intellij.psi.util.PsiTreeUtil
trait SmartCompletionData{
fun accepts(descriptor: DeclarationDescriptor): Boolean
@@ -49,6 +55,7 @@ fun buildSmartCompletionData(expression: JetSimpleNameExpression, resolveSession
if (receiver == null) {
typeInstantiationItems(expectedType, resolveSession, bindingContext).toCollection(additionalElements)
thisItems(expressionWithType, expectedType, bindingContext).toCollection(additionalElements)
staticMembers(expressionWithType, expectedType, resolveSession, bindingContext).toCollection(additionalElements)
}
val dataFlowInfo = bindingContext.get(BindingContext.EXPRESSION_DATA_FLOW_INFO, expressionWithType)
@@ -231,6 +238,59 @@ private fun processDataFlowInfo(dataFlowInfo: DataFlowInfo?, receiver: JetExpres
return ProcessDataFlowInfoResult()
}
// adds java static members, enum members and members from class object
private fun staticMembers(context: JetExpression, expectedType: JetType, resolveSession: CancelableResolveSession, bindingContext: BindingContext): Iterable<LookupElement> {
val classDescriptor = TypeUtils.getClassDescriptor(expectedType)
if (classDescriptor == null) return listOf()
if (classDescriptor.getName().isSpecial()) return listOf()
val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, context)
if (scope == null) return listOf()
val descriptors = ArrayList<DeclarationDescriptor>()
val isSuitableCallable: (DeclarationDescriptor) -> Boolean = { it is CallableDescriptor && it.getReturnType()?.let { JetTypeChecker.INSTANCE.isSubtypeOf(it, expectedType) } ?: false }
if (classDescriptor is JavaClassDescriptor) {
//TODO: shouldn't we have special util to obtain this pseudo package?
val container = classDescriptor.getContainingDeclaration() //TODO: nested classes!
if (container is NamespaceDescriptor) {
val pseudoPackage: NamespaceDescriptor? = container.getMemberScope().getNamespace(classDescriptor.getName())
if (pseudoPackage != null) {
pseudoPackage.getMemberScope().getAllDescriptors().filterTo(descriptors, isSuitableCallable)
}
}
}
val classObject = classDescriptor.getClassObjectDescriptor()
if (classObject != null) {
classObject.getDefaultType().getMemberScope().getAllDescriptors().filterTo(descriptors, isSuitableCallable)
}
if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
classDescriptor.getDefaultType().getMemberScope().getAllDescriptors()
.filterTo(descriptors) { it is ClassDescriptor && it.getKind() == ClassKind.ENUM_ENTRY && JetTypeChecker.INSTANCE.isSubtypeOf(it.getDefaultType(), expectedType) }
}
return descriptors
.filter { !(it is DeclarationDescriptorWithVisibility) || Visibilities.isVisible(it, scope.getContainingDeclaration()) }
.map {
val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, it)
val presentation = LookupElementPresentation()
lookupElement.renderElement(presentation)
var builder = LookupElementBuilder.create(lookupElement.getObject(), classDescriptor.getName().asString() + "." + lookupElement.getLookupString())
.withIcon(presentation.getIcon())
.withStrikeoutness(presentation.isStrikeout())
.withTailText(" (" + DescriptorUtils.getFQName(classDescriptor.getContainingDeclaration()) + ")")
.withTypeText(if (presentation.getTypeText() != "") presentation.getTypeText() else DescriptorRenderer.TEXT.renderType(classDescriptor.getDefaultType()))
if (it is FunctionDescriptor) {
builder = builder.withPresentableText(builder.getLookupString() + "()")
val caretPosition = if (it.getValueParameters().empty) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS
builder = builder.withInsertHandler(JetFunctionInsertHandler(caretPosition, BracketType.PARENTHESIS))
}
builder
}
}
private fun <T : Any> T?.toList(): List<T> = if (this != null) listOf(this) else listOf()
private fun <T> MutableCollection<T>.addAll(iterator: Iterator<T>) {
@@ -0,0 +1,11 @@
package sample
class K {
class object {
fun bar(): K = K()
}
}
fun foo(){
val k : K = <caret>
}
@@ -0,0 +1,11 @@
package sample
class K {
class object {
fun bar(): K = K()
}
}
fun foo(){
val k : K = K.bar()<caret>
}
@@ -0,0 +1,11 @@
package sample
class K {
class object {
fun bar(p: Int): K = K()
}
}
fun foo(){
val k : K = <caret>
}
@@ -0,0 +1,11 @@
package sample
class K {
class object {
fun bar(p: Int): K = K()
}
}
fun foo(){
val k : K = K.bar(<caret>)
}
@@ -0,0 +1,3 @@
fun foo(){
var l : java.util.Locale = <caret>
}
@@ -0,0 +1,5 @@
import java.util.Locale
fun foo(){
var l : java.util.Locale = Locale.ENGLISH
}
@@ -0,0 +1,3 @@
fun foo(){
val l : java.lang.Thread = <caret>
}
@@ -0,0 +1,3 @@
fun foo(){
val l : java.lang.Thread = Thread.currentThread()<caret>
}
@@ -0,0 +1,21 @@
package sample
class K {
class object {
val foo: K = K()
fun bar(): K = K()
val x: String = ""
var kk: K? = null
private val privateVal: K = K()
}
}
fun foo(){
val k : K = <caret>
}
// EXIST: { lookupString:"K.foo", itemText:"K.foo", tailText:" (sample)", typeText:"sample.K" }
// EXIST: { lookupString:"K.bar", itemText:"K.bar()", tailText:" (sample)", typeText:"sample.K" }
// ABSENT: K.x
// ABSENT: K.kk
// ABSENT: K.privateVal
@@ -0,0 +1,19 @@
package sample
class K {
class object {
val foo: K = K()
fun bar(): K = K()
val x: String = ""
var kk: K? = null
}
}
fun foo(){
val k : K? = <caret>
}
// EXIST: { lookupString:"K.foo", itemText:"K.foo", tailText:" (sample)", typeText:"sample.K" }
// EXIST: { lookupString:"K.bar", itemText:"K.bar()", tailText:" (sample)", typeText:"sample.K" }
// ABSENT: K.x
// EXIST: { lookupString:"K.kk", itemText:"K.kk", tailText:" (sample)", typeText:"sample.K?" }
@@ -0,0 +1,13 @@
package sample
enum class Foo {
X
Y
}
fun foo(){
val f : Foo = <caret>
}
// EXIST: { lookupString:"Foo.X", itemText:"Foo.X", tailText:" (sample)", typeText:"sample.Foo" }
// EXIST: { lookupString:"Foo.Y", itemText:"Foo.Y", tailText:" (sample)", typeText:"sample.Foo" }
@@ -0,0 +1,8 @@
import java.lang.annotation.ElementType
fun foo(){
val e : ElementType = <caret>
}
// EXIST: { lookupString:"ElementType.TYPE", itemText:"ElementType.TYPE", tailText:" (java.lang.annotation)", typeText:"java.lang.annotation.ElementType" }
// EXIST: { lookupString:"ElementType.FIELD", itemText:"ElementType.FIELD", tailText:" (java.lang.annotation)", typeText:"java.lang.annotation.ElementType" }
@@ -0,0 +1,8 @@
import java.lang.annotation.ElementType
fun foo(){
var e : ElementType? = <caret>
}
// EXIST: { lookupString:"ElementType.TYPE", itemText:"ElementType.TYPE", tailText:" (java.lang.annotation)", typeText:"java.lang.annotation.ElementType" }
// EXIST: { lookupString:"ElementType.FIELD", itemText:"ElementType.FIELD", tailText:" (java.lang.annotation)", typeText:"java.lang.annotation.ElementType" }
@@ -0,0 +1,6 @@
fun foo(){
var l : java.util.Locale = <caret>
}
// EXIST: { lookupString:"Locale.ENGLISH", itemText:"Locale.ENGLISH", tailText:" (java.util)", typeText:"Locale" }
// EXIST: { lookupString:"Locale.FRENCH", itemText:"Locale.FRENCH", tailText:" (java.util)", typeText:"Locale" }
@@ -0,0 +1,6 @@
fun foo(){
var l : java.util.Locale? = <caret>
}
// EXIST: { lookupString:"Locale.ENGLISH", itemText:"Locale.ENGLISH", tailText:" (java.util)", typeText:"Locale" }
// EXIST: { lookupString:"Locale.FRENCH", itemText:"Locale.FRENCH", tailText:" (java.util)", typeText:"Locale" }
@@ -0,0 +1,5 @@
fun foo(){
val l : java.lang.Thread = <caret>
}
// EXIST: { lookupString:"Thread.currentThread", itemText:"Thread.currentThread()", tailText:" (java.lang)", typeText:"Thread" }
@@ -61,6 +61,16 @@ public class JetSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest("idea/testData/completion/smart/ChainedCall.kt");
}
@TestMetadata("ClassObjectMembers.kt")
public void testClassObjectMembers() throws Exception {
doTest("idea/testData/completion/smart/ClassObjectMembers.kt");
}
@TestMetadata("ClassObjectMembersForNullable.kt")
public void testClassObjectMembersForNullable() throws Exception {
doTest("idea/testData/completion/smart/ClassObjectMembersForNullable.kt");
}
@TestMetadata("Constructor.kt")
public void testConstructor() throws Exception {
doTest("idea/testData/completion/smart/Constructor.kt");
@@ -81,11 +91,41 @@ public class JetSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest("idea/testData/completion/smart/EmptyPrefix.kt");
}
@TestMetadata("EnumMembers.kt")
public void testEnumMembers() throws Exception {
doTest("idea/testData/completion/smart/EnumMembers.kt");
}
@TestMetadata("InsideIdentifier.kt")
public void testInsideIdentifier() throws Exception {
doTest("idea/testData/completion/smart/InsideIdentifier.kt");
}
@TestMetadata("JavaEnumMembers.kt")
public void testJavaEnumMembers() throws Exception {
doTest("idea/testData/completion/smart/JavaEnumMembers.kt");
}
@TestMetadata("JavaEnumMembersForNullable.kt")
public void testJavaEnumMembersForNullable() throws Exception {
doTest("idea/testData/completion/smart/JavaEnumMembersForNullable.kt");
}
@TestMetadata("JavaStaticFields.kt")
public void testJavaStaticFields() throws Exception {
doTest("idea/testData/completion/smart/JavaStaticFields.kt");
}
@TestMetadata("JavaStaticFieldsForNullable.kt")
public void testJavaStaticFieldsForNullable() throws Exception {
doTest("idea/testData/completion/smart/JavaStaticFieldsForNullable.kt");
}
@TestMetadata("JavaStaticMethods.kt")
public void testJavaStaticMethods() throws Exception {
doTest("idea/testData/completion/smart/JavaStaticMethods.kt");
}
@TestMetadata("MethodCallArgument.kt")
public void testMethodCallArgument() throws Exception {
doTest("idea/testData/completion/smart/MethodCallArgument.kt");
@@ -42,4 +42,8 @@ public class SmartCompletionHandlerTest() : CompletionHandlerTestBase() {
fun testConstructorForNullable() = doTest()
fun testConstructorForJavaClass() = doTest()
//fun testConstructorInsertsImport() = doTest() //TODO
fun testJavaStaticMethod() = doTest(1, "Thread.currentThread", null, '\n')
fun testClassObjectMethod1() = doTest(1, "K.bar", null, '\n')
fun testClassObjectMethod2() = doTest(1, "K.bar", null, '\n')
//fun testJavaStaticFieldInsertImport() = doTest(1, "Locale.ENGLISH", null, '\n') //TODO
}