Smart completion: anonymous object creation + rewrote some code using LookupElementDecorator

This commit is contained in:
Valentin Kipyatkov
2013-12-20 18:48:52 +04:00
parent 9627a335c2
commit 914a566094
16 changed files with 177 additions and 70 deletions
@@ -6,6 +6,17 @@
<item name='com.intellij.codeInsight.lookup.Lookup com.intellij.openapi.editor.Editor getEditor()'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.codeInsight.lookup.LookupElement void handleInsert(com.intellij.codeInsight.completion.InsertionContext) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElement void renderElement(com.intellij.codeInsight.lookup.LookupElementPresentation) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElement withAutoCompletionPolicy(com.intellij.codeInsight.lookup.AutoCompletionPolicy)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder create(com.intellij.psi.PsiNamedElement)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
@@ -42,6 +53,10 @@
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 withRenderer(com.intellij.codeInsight.lookup.LookupElementRenderer&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 withStrikeoutness(boolean)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
@@ -50,6 +65,10 @@
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withTailText(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withTailText(java.lang.String, boolean)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withTypeText(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
@@ -58,6 +77,20 @@
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withTypeText(java.lang.String, boolean)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementDecorator void renderElement(com.intellij.codeInsight.lookup.LookupElementPresentation) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementRenderer void renderElement(T, com.intellij.codeInsight.lookup.LookupElementPresentation)'>
<annotation name='jet.runtime.typeinfo.KotlinSignature'>
<val name="value" val="&quot;fun renderElement(element: T?, presentation: LookupElementPresentation): Unit&quot;"/>
</annotation>
</item>
<item
name='com.intellij.codeInsight.lookup.LookupElementRenderer void renderElement(T, com.intellij.codeInsight.lookup.LookupElementPresentation) 1'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.lookup.MutableLookupElement com.intellij.codeInsight.lookup.MutableLookupElement&lt;T&gt; setInsertHandler(com.intellij.codeInsight.completion.InsertHandler&lt;? extends com.intellij.codeInsight.lookup.LookupElement&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
@@ -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<ConstructorDescriptor> = 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<LookupElement>
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<LookupElement>{
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<ConstructorDescriptor> = 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>(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<LookupElement> {
@@ -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<LookupElement>?
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>(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{
@@ -0,0 +1,3 @@
trait Foo
var a : Foo = <caret>
@@ -0,0 +1,3 @@
trait Foo
var a : Foo = object: Foo {<caret>}
@@ -0,0 +1,3 @@
abstract class Foo
var a : Foo = <caret>
@@ -0,0 +1,3 @@
abstract class Foo
var a : Foo = object: Foo() {<caret>}
@@ -0,0 +1 @@
var r : Runnable = <caret>
@@ -0,0 +1,5 @@
var r : Runnable = object: Runnable {
override fun run() {
throw UnsupportedOperationException()
}
}
@@ -0,0 +1,6 @@
trait Foo
var a : Foo = <caret>
// ABSENT: Foo
// EXIST: { lookupString:"object", itemText:"object: Foo{...}" }
@@ -0,0 +1,6 @@
abstract class Foo
var a : Foo = <caret>
// ABSENT: Foo
// EXIST: { lookupString:"object", itemText:"object: Foo(){...}" }
@@ -0,0 +1,4 @@
var a : Appendable = <caret>
// ABSENT: Appendable
// EXIST: { lookupString:"object", itemText:"object: Appendable{...}" }
@@ -1,7 +0,0 @@
abstract class Foo
fun foo(p : Any){
var a : Foo = <caret>
}
// ABSENT: Foo
@@ -1,3 +0,0 @@
var a : Appendable = <caret>
// ABSENT: Appendable
@@ -1,7 +0,0 @@
trait Foo
fun foo(p : Any){
var a : Foo = <caret>
}
// ABSENT: Foo
@@ -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");
@@ -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')
}