Smart completion: java static members, enum members and class object members added
This commit is contained in:
@@ -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<com.intellij.codeInsight.lookup.LookupElement>)'>
|
||||
<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();
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user