diff --git a/annotations/com/intellij/codeInsight/lookup/annotations.xml b/annotations/com/intellij/codeInsight/lookup/annotations.xml index 2c3b30ff6ac..8ad65130b35 100644 --- a/annotations/com/intellij/codeInsight/lookup/annotations.xml +++ b/annotations/com/intellij/codeInsight/lookup/annotations.xml @@ -6,6 +6,17 @@ + + + + + + + + + @@ -42,6 +53,10 @@ name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withPresentableText(java.lang.String)'> + + + @@ -50,6 +65,10 @@ name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withTailText(java.lang.String)'> + + + @@ -58,6 +77,20 @@ name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withTypeText(java.lang.String, boolean)'> + + + + + + + + + + + diff --git a/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt b/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt index 96ef59ea5ce..aeff3a5e59a 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt @@ -26,6 +26,9 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiClass import com.intellij.psi.PsiModifier import com.intellij.psi.util.PsiTreeUtil +import com.intellij.openapi.editor.EditorModificationUtil +import org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler +import com.intellij.psi.PsiDocumentManager trait SmartCompletionData{ fun accepts(descriptor: DeclarationDescriptor): Boolean @@ -122,12 +125,13 @@ private fun typeInstantiationItems(expectedType: JetType, resolveSession: Cancel val typeConstructor: TypeConstructor = expectedType.getConstructor() val classifier: ClassifierDescriptor? = typeConstructor.getDeclarationDescriptor() if (!(classifier is ClassDescriptor)) return listOf() - if (classifier.getModality() == Modality.ABSTRACT) return listOf() //TODO: check for constructor's visibility val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, classifier) + var lookupString = lookupElement.getLookupString() + val typeArgs = expectedType.getArguments() //TODO: shouldn't be method in DescriptorRenderer to render type arguments? val typeArgsText = @@ -135,27 +139,59 @@ private fun typeInstantiationItems(expectedType: JetType, resolveSession: Cancel "" else typeArgs.map { DescriptorRenderer.TEXT.renderType(it.getType()) }.makeString(", ", "<", ">") - val presentableText = lookupElement.getLookupString() + typeArgsText + "()" + var itemText = lookupElement.getLookupString() + typeArgsText - val constructors: Collection = classifier.getConstructors() - val caretPosition = - if (constructors.size == 0) - CaretPosition.AFTER_BRACKETS - else if (constructors.size == 1) - if (constructors.first().getValueParameters().isEmpty()) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS - else - CaretPosition.IN_BRACKETS - val insertHandler = JetFunctionInsertHandler(caretPosition, BracketType.PARENTHESIS) - - //TODO: very bad code - if (lookupElement is LookupElementBuilder) { - return listOf(lookupElement.withPresentableText(presentableText).withInsertHandler(insertHandler)) + val insertHandler: InsertHandler + var suppressAutoInsertion: Boolean = false + if (classifier.getModality() == Modality.ABSTRACT) { + val constructorParenthesis = if (classifier.getKind() != ClassKind.TRAIT) "()" else "" + itemText += constructorParenthesis + itemText = "object: " + itemText + "{...}" + lookupString = "object" //? + insertHandler = object: InsertHandler{ + override fun handleInsert(context: InsertionContext, item: LookupElement) { + //TODO: insert import + val editor = context.getEditor() + EditorModificationUtil.insertStringAtCaret(editor, ": " + classifier.getName() + constructorParenthesis + " {}") + editor.getCaretModel().moveToOffset(editor.getCaretModel().getOffset() - 1) + PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments(); + ImplementMethodsHandler().invoke(context.getProject(), editor, context.getFile(), true) + } + } + suppressAutoInsertion = true } - else if (lookupElement is JavaPsiClassReferenceElement) { - return listOf(lookupElement.setPresentableText(presentableText).setInsertHandler(insertHandler)) + else { + itemText += "()" + val constructors: Collection = classifier.getConstructors() + val caretPosition = + if (constructors.size == 0) + CaretPosition.AFTER_BRACKETS + else if (constructors.size == 1) + if (constructors.first().getValueParameters().isEmpty()) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS + else + CaretPosition.IN_BRACKETS + insertHandler = JetFunctionInsertHandler(caretPosition, BracketType.PARENTHESIS) } - return listOf() + val lookupElementDecorated = object: LookupElementDecorator(lookupElement){ + override fun getLookupString() = lookupString + + override fun renderElement(presentation: LookupElementPresentation) { + lookupElement.renderElement(presentation) + presentation.setItemText(itemText) + } + + override fun handleInsert(context: InsertionContext) { + insertHandler.handleInsert(context, lookupElement) + } + } + + if (suppressAutoInsertion) { + return listOf(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(lookupElementDecorated)) + } + else{ + return listOf(lookupElementDecorated) + } } private fun thisItems(context: JetExpression, expectedType: JetType, bindingContext: BindingContext): Iterable { @@ -273,27 +309,45 @@ private fun staticMembers(context: JetExpression, expectedType: JetType, resolve .filterTo(descriptors) { it is ClassDescriptor && it.getKind() == ClassKind.ENUM_ENTRY } } - 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().isNullOrEmpty()) - 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 + fun toLookupElement(descriptor: DeclarationDescriptor): LookupElement { + val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor) + val lookupString = classDescriptor.getName().asString() + "." + lookupElement.getLookupString() + var itemText = lookupString + val insertHandler: InsertHandler? + if (descriptor is FunctionDescriptor) { + itemText += "()" + val caretPosition = if (descriptor.getValueParameters().empty) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS + insertHandler = JetFunctionInsertHandler(caretPosition, BracketType.PARENTHESIS) + } + else { + insertHandler = null + } + return object: LookupElementDecorator(lookupElement){ + override fun getLookupString() = lookupString + + override fun renderElement(presentation: LookupElementPresentation) { + lookupElement.renderElement(presentation) + presentation.setItemText(itemText) + presentation.setTailText(" (" + DescriptorUtils.getFqName(classDescriptor.getContainingDeclaration()) + ")") + if (presentation.getTypeText().isNullOrEmpty()) { + presentation.setTypeText(DescriptorRenderer.TEXT.renderType(classDescriptor.getDefaultType())) } + } + + override fun handleInsert(context: InsertionContext) { + if (insertHandler != null) { + insertHandler.handleInsert(context, lookupElement) + } + else{ + lookupElement.handleInsert(context) + } + } + } + } + + return descriptors + .filter { !(it is DeclarationDescriptorWithVisibility) || Visibilities.isVisible(it, scope.getContainingDeclaration()) } + .map({toLookupElement(it)}) //TODO: use ::toLookupElement but it causes compiler to crash } private fun isSubtypeOf(t: JetType, expectedType: JetType): Boolean{ diff --git a/idea/testData/completion/handlers/smart/AnonymousObject1.kt b/idea/testData/completion/handlers/smart/AnonymousObject1.kt new file mode 100644 index 00000000000..e7b59ef3ae0 --- /dev/null +++ b/idea/testData/completion/handlers/smart/AnonymousObject1.kt @@ -0,0 +1,3 @@ +trait Foo + +var a : Foo = diff --git a/idea/testData/completion/handlers/smart/AnonymousObject1.kt.after b/idea/testData/completion/handlers/smart/AnonymousObject1.kt.after new file mode 100644 index 00000000000..e16a0ad3ec7 --- /dev/null +++ b/idea/testData/completion/handlers/smart/AnonymousObject1.kt.after @@ -0,0 +1,3 @@ +trait Foo + +var a : Foo = object: Foo {} diff --git a/idea/testData/completion/handlers/smart/AnonymousObject2.kt b/idea/testData/completion/handlers/smart/AnonymousObject2.kt new file mode 100644 index 00000000000..824e20a25de --- /dev/null +++ b/idea/testData/completion/handlers/smart/AnonymousObject2.kt @@ -0,0 +1,3 @@ +abstract class Foo + +var a : Foo = diff --git a/idea/testData/completion/handlers/smart/AnonymousObject2.kt.after b/idea/testData/completion/handlers/smart/AnonymousObject2.kt.after new file mode 100644 index 00000000000..3add17a6704 --- /dev/null +++ b/idea/testData/completion/handlers/smart/AnonymousObject2.kt.after @@ -0,0 +1,3 @@ +abstract class Foo + +var a : Foo = object: Foo() {} diff --git a/idea/testData/completion/handlers/smart/AnonymousObject3.kt b/idea/testData/completion/handlers/smart/AnonymousObject3.kt new file mode 100644 index 00000000000..8fcf46277a5 --- /dev/null +++ b/idea/testData/completion/handlers/smart/AnonymousObject3.kt @@ -0,0 +1 @@ +var r : Runnable = diff --git a/idea/testData/completion/handlers/smart/AnonymousObject3.kt.after b/idea/testData/completion/handlers/smart/AnonymousObject3.kt.after new file mode 100644 index 00000000000..0598254f494 --- /dev/null +++ b/idea/testData/completion/handlers/smart/AnonymousObject3.kt.after @@ -0,0 +1,5 @@ +var r : Runnable = object: Runnable { + override fun run() { + throw UnsupportedOperationException() + } +} diff --git a/idea/testData/completion/smart/AnonymousObject1.kt b/idea/testData/completion/smart/AnonymousObject1.kt new file mode 100644 index 00000000000..fc0486b0cb4 --- /dev/null +++ b/idea/testData/completion/smart/AnonymousObject1.kt @@ -0,0 +1,6 @@ +trait Foo + +var a : Foo = + +// ABSENT: Foo +// EXIST: { lookupString:"object", itemText:"object: Foo{...}" } diff --git a/idea/testData/completion/smart/AnonymousObject2.kt b/idea/testData/completion/smart/AnonymousObject2.kt new file mode 100644 index 00000000000..74ee08a1b9c --- /dev/null +++ b/idea/testData/completion/smart/AnonymousObject2.kt @@ -0,0 +1,6 @@ +abstract class Foo + +var a : Foo = + +// ABSENT: Foo +// EXIST: { lookupString:"object", itemText:"object: Foo(){...}" } diff --git a/idea/testData/completion/smart/AnonymousObjectForJavaInterface.kt b/idea/testData/completion/smart/AnonymousObjectForJavaInterface.kt new file mode 100644 index 00000000000..326935a2fe6 --- /dev/null +++ b/idea/testData/completion/smart/AnonymousObjectForJavaInterface.kt @@ -0,0 +1,4 @@ +var a : Appendable = + +// ABSENT: Appendable +// EXIST: { lookupString:"object", itemText:"object: Appendable{...}" } \ No newline at end of file diff --git a/idea/testData/completion/smart/NoConstructorForAbstract.kt b/idea/testData/completion/smart/NoConstructorForAbstract.kt deleted file mode 100644 index 0b18a4584ca..00000000000 --- a/idea/testData/completion/smart/NoConstructorForAbstract.kt +++ /dev/null @@ -1,7 +0,0 @@ -abstract class Foo - -fun foo(p : Any){ - var a : Foo = -} - -// ABSENT: Foo diff --git a/idea/testData/completion/smart/NoConstructorForJavaInterface.kt b/idea/testData/completion/smart/NoConstructorForJavaInterface.kt deleted file mode 100644 index 7dfd234c1c9..00000000000 --- a/idea/testData/completion/smart/NoConstructorForJavaInterface.kt +++ /dev/null @@ -1,3 +0,0 @@ -var a : Appendable = - -// ABSENT: Appendable diff --git a/idea/testData/completion/smart/NoConstructorForTrait.kt b/idea/testData/completion/smart/NoConstructorForTrait.kt deleted file mode 100644 index 26d16b3d75a..00000000000 --- a/idea/testData/completion/smart/NoConstructorForTrait.kt +++ /dev/null @@ -1,7 +0,0 @@ -trait Foo - -fun foo(p : Any){ - var a : Foo = -} - -// ABSENT: Foo diff --git a/idea/tests/org/jetbrains/jet/completion/JvmSmartCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/JvmSmartCompletionTestGenerated.java index 0adda257811..a1e85016c9a 100644 --- a/idea/tests/org/jetbrains/jet/completion/JvmSmartCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/JvmSmartCompletionTestGenerated.java @@ -36,6 +36,21 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/completion/smart"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("AnonymousObject1.kt") + public void testAnonymousObject1() throws Exception { + doTest("idea/testData/completion/smart/AnonymousObject1.kt"); + } + + @TestMetadata("AnonymousObject2.kt") + public void testAnonymousObject2() throws Exception { + doTest("idea/testData/completion/smart/AnonymousObject2.kt"); + } + + @TestMetadata("AnonymousObjectForJavaInterface.kt") + public void testAnonymousObjectForJavaInterface() throws Exception { + doTest("idea/testData/completion/smart/AnonymousObjectForJavaInterface.kt"); + } + @TestMetadata("AutoCastedType.kt") public void testAutoCastedType() throws Exception { doTest("idea/testData/completion/smart/AutoCastedType.kt"); @@ -151,21 +166,6 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT doTest("idea/testData/completion/smart/MethodCallArgument.kt"); } - @TestMetadata("NoConstructorForAbstract.kt") - public void testNoConstructorForAbstract() throws Exception { - doTest("idea/testData/completion/smart/NoConstructorForAbstract.kt"); - } - - @TestMetadata("NoConstructorForJavaInterface.kt") - public void testNoConstructorForJavaInterface() throws Exception { - doTest("idea/testData/completion/smart/NoConstructorForJavaInterface.kt"); - } - - @TestMetadata("NoConstructorForTrait.kt") - public void testNoConstructorForTrait() throws Exception { - doTest("idea/testData/completion/smart/NoConstructorForTrait.kt"); - } - @TestMetadata("NoConstructorWithQualifier.kt") public void testNoConstructorWithQualifier() throws Exception { doTest("idea/testData/completion/smart/NoConstructorWithQualifier.kt"); diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTest.kt b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTest.kt index 5e05779187d..5faa45822fb 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTest.kt +++ b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTest.kt @@ -51,4 +51,7 @@ public class SmartCompletionHandlerTest() : CompletionHandlerTestBase() { fun testTabReplaceExpression2() = doTest(1, "sss", null, '\t') fun testTabReplaceExpression3() = doTest(1, "sss", null, '\t') fun testTabReplaceOperand() = doTest(1, "b3", null, '\t') + fun testAnonymousObject1() = doTest(1, "object", null, '\t') + fun testAnonymousObject2() = doTest(1, "object", null, '\t') + fun testAnonymousObject3() = doTest(1, "object", null, '\t') }