Completion: moved signatures from "item text" to tail

#KT-5652 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-11-14 21:23:05 +03:00
parent b2f2c9ac75
commit 7410c36e3a
32 changed files with 112 additions and 61 deletions
@@ -30,6 +30,7 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
import com.intellij.codeInsight.completion.PrefixMatcher
import org.jetbrains.jet.lang.resolve.name.Name
import com.intellij.codeInsight.lookup.LookupElementPresentation
enum class ItemPriority {
MULTIPLE_ARGUMENTS_ITEM
@@ -79,4 +80,11 @@ private fun qualifierName(descriptor: DeclarationDescriptor): String? = when (de
else -> null
}
fun PrefixMatcher.asNameFilter() = { (name: Name) -> !name.isSpecial() && prefixMatches(name.getIdentifier()) }
fun PrefixMatcher.asNameFilter() = { (name: Name) -> !name.isSpecial() && prefixMatches(name.getIdentifier()) }
fun LookupElementPresentation.prependTailText(text: String, grayed: Boolean) {
val tails = getTailFragments()
clearTail()
appendTailText(text, grayed)
tails.forEach { appendTailText(it.text, it.isGrayed()) }
}
@@ -58,40 +58,43 @@ public object KotlinLookupElementFactory {
return createLookupElementForJavaClass(declaration)
}
val name = descriptor.getName().asString()
var element = LookupElementBuilder.create(DeclarationDescriptorLookupObject(descriptor, analyzer, declaration), name)
var presentableText = name
var typeText = ""
var tailText = ""
var element = LookupElementBuilder.create(DeclarationDescriptorLookupObject(descriptor, analyzer, declaration), descriptor.getName().asString())
.withIcon(JetDescriptorIconProvider.getIcon(descriptor, declaration, Iconable.ICON_FLAG_VISIBILITY))
if (descriptor is FunctionDescriptor) {
val returnType = descriptor.getReturnType()
typeText = if (returnType != null) DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(returnType) else ""
presentableText += DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderFunctionParameters(descriptor)
when (descriptor) {
is FunctionDescriptor -> {
val returnType = descriptor.getReturnType()
element = element.withTypeText(if (returnType != null) DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(returnType) else "")
element = element.appendTailText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderFunctionParameters(descriptor), false)
if (descriptor.getExtensionReceiverParameter() != null) {
tailText += " for " + DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getExtensionReceiverParameter()!!.getType())
tailText += " in " + DescriptorUtils.getFqName(descriptor.getContainingDeclaration())
if (descriptor.getExtensionReceiverParameter() != null) {
val tail = " for " + DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getExtensionReceiverParameter()!!.getType()) +
" in " + DescriptorUtils.getFqName(descriptor.getContainingDeclaration())
element = element.appendTailText(tail, true)
}
}
is VariableDescriptor -> {
element = element.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getType()))
}
is ClassDescriptor -> {
element = element.appendTailText(" (" + DescriptorUtils.getFqName(descriptor.getContainingDeclaration()) + ")", true)
}
else -> {
element = element.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor))
}
}
else if (descriptor is VariableDescriptor) {
typeText = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getType())
}
else if (descriptor is ClassDescriptor) {
tailText = " (" + DescriptorUtils.getFqName(descriptor.getContainingDeclaration()) + ")"
}
else {
typeText = DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor)
if (KotlinBuiltIns.getInstance().isDeprecated(descriptor)) {
element = element.withStrikeoutness(true)
}
val insertHandler = getDefaultInsertHandler(descriptor)
element = element.withInsertHandler(insertHandler)
element = element.withTailText(tailText, true).withTypeText(typeText).withPresentableText(presentableText)
element = element.withIcon(JetDescriptorIconProvider.getIcon(descriptor, declaration, Iconable.ICON_FLAG_VISIBILITY))
element = element.withStrikeoutness(KotlinBuiltIns.getInstance().isDeprecated(descriptor))
if (insertHandler is KotlinFunctionInsertHandler && insertHandler.lambdaInfo != null) {
element.putUserData<Boolean>(KotlinCompletionCharFilter.ACCEPT_OPENING_BRACE, true)
}
@@ -80,7 +80,11 @@ class LookupElementsCollector(
addElement(object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.setItemText(getLookupString() + " " + buildLambdaPresentation(parameterType))
val tails = presentation.getTailFragments()
presentation.clearTail()
presentation.appendTailText(" " + buildLambdaPresentation(parameterType), false)
tails.drop(1)/*drop old function signature*/.forEach { presentation.appendTailText(it.text, it.isGrayed()) }
}
override fun handleInsert(context: InsertionContext) {
@@ -246,7 +246,8 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.setItemText(text)
presentation.setTypeText("")
presentation.clearTail()
presentation.setTypeText(null)
}
override fun handleInsert(context: InsertionContext) {
@@ -74,6 +74,7 @@ class TypeInstantiationItems(val resolveSession: ResolveSessionForBodies, val bi
val typeArgs = jetType.getArguments()
var itemText = lookupString + DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderTypeArguments(typeArgs)
var signatureText: String? = null
val insertHandler: InsertHandler<LookupElement>
val typeText = qualifiedNameForSourceCode(classifier) + IdeDescriptorRenderers.SOURCE_CODE.renderTypeArguments(typeArgs)
@@ -99,7 +100,7 @@ class TypeInstantiationItems(val resolveSession: ResolveSessionForBodies, val bi
}
else {
//TODO: when constructor has one parameter of lambda type with more than one parameter, generate special additional item
itemText += when (visibleConstructors.size) {
signatureText = when (visibleConstructors.size) {
0 -> "()"
1 -> DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderFunctionParameters(visibleConstructors.single())
else -> "(...)"
@@ -138,6 +139,10 @@ class TypeInstantiationItems(val resolveSession: ResolveSessionForBodies, val bi
override fun renderElement(presentation: LookupElementPresentation) {
getDelegate().renderElement(presentation)
presentation.setItemText(itemText)
if (signatureText != null) {
presentation.prependTailText(signatureText!!, false)
}
}
override fun handleInsert(context: InsertionContext) {
@@ -4,4 +4,4 @@ fun some() {
tes<caret>
}
// EXIST: { lookupString:"test", itemText:"test(a: Int)" }
// EXIST: { lookupString:"test", itemText:"test", tailText:"(a: Int)" }
@@ -4,5 +4,5 @@ fun test() {
fo<caret>
}
// EXIST: { lookupString:"foo", itemText: "foo(p: (String, Char) -> Unit)", typeText:"Unit" }
// EXIST: { lookupString:"foo", itemText: "foo { (String, Char) -> ... }", typeText:"Unit" }
// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(p: (String, Char) -> Unit)", typeText:"Unit" }
// EXIST: { lookupString:"foo", itemText: "foo", tailText: " { (String, Char) -> ... }", typeText:"Unit" }
@@ -0,0 +1,8 @@
fun String.foo(p: (String, Char) -> Unit){}
fun test() {
"".fo<caret>
}
// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(p: (String, Char) -> Unit) for String in <root>", typeText:"Unit" }
// EXIST: { lookupString:"foo", itemText: "foo", tailText: " { (String, Char) -> ... } for String in <root>", typeText:"Unit" }
@@ -8,5 +8,5 @@ fun firstFun() {
}
// INVOCATION_COUNT: 1
// EXIST: { lookupString:"toLinkedList", itemText:"toLinkedList()", tailText:" for Iterable<T> in kotlin" }
// EXIST: { lookupString:"toLinkedList", itemText:"toLinkedList", tailText:"() for Iterable<T> in kotlin" }
// NUMBER: 1
@@ -2,4 +2,5 @@ fun foo(){
val l : java.util.Calendar = <caret>
}
// ELEMENT_TEXT: Calendar.getInstance(TimeZone!)
// ELEMENT_TEXT: Calendar.getInstance
// TAIL_TEXT: (TimeZone!)
@@ -4,4 +4,5 @@ fun foo(){
val l : java.util.Calendar = Calendar.getInstance(<caret>)
}
// ELEMENT_TEXT: Calendar.getInstance(TimeZone!)
// ELEMENT_TEXT: Calendar.getInstance
// TAIL_TEXT: (TimeZone!)
@@ -6,4 +6,5 @@ fun bar() {
foo(<caret>)
}
// ELEMENT_TEXT: "?: getString(i: Int)"
// ELEMENT_TEXT: "?: getString"
// TAIL_TEXT: "(i: Int)"
@@ -6,4 +6,5 @@ fun bar() {
foo(getString(<caret>) ?: )
}
// ELEMENT_TEXT: "?: getString(i: Int)"
// ELEMENT_TEXT: "?: getString"
// TAIL_TEXT: "(i: Int)"
@@ -8,4 +8,5 @@ fun foo(){
val k : K = <caret>
}
// ELEMENT_TEXT: "!! K.bar()"
// ELEMENT_TEXT: "!! K.bar"
// TAIL_TEXT: "()"
@@ -8,4 +8,5 @@ fun foo(){
val k : K = K.bar()!!<caret>
}
// ELEMENT_TEXT: "!! K.bar()"
// ELEMENT_TEXT: "!! K.bar"
// TAIL_TEXT: "()"
@@ -15,7 +15,7 @@ fun foo(){
}
// EXIST: { lookupString:"foo", itemText:"K.foo", tailText:" (sample)", typeText:"K" }
// EXIST: { lookupString:"bar", itemText:"K.bar()", tailText:" (sample)", typeText:"K" }
// EXIST: { lookupString:"bar", itemText:"K.bar", tailText:"() (sample)", typeText:"K" }
// ABSENT: { itemText: "K.x" }
// ABSENT: { itemText:"K.kk" }
// EXIST: { lookupString:"kk", itemText:"!! K.kk", tailText:" (sample)", typeText:"K?" }
@@ -14,6 +14,6 @@ fun foo(){
}
// EXIST: { lookupString:"foo", itemText:"K.foo", tailText:" (sample)", typeText:"K" }
// EXIST: { lookupString:"bar", itemText:"K.bar()", tailText:" (sample)", typeText:"K" }
// EXIST: { lookupString:"bar", itemText:"K.bar", tailText:"() (sample)", typeText:"K" }
// ABSENT: { itemText: "K.x" }
// EXIST: { lookupString:"kk", itemText:"K.kk", tailText:" (sample)", typeText:"K?" }
@@ -2,4 +2,4 @@ class Foo
var a : Foo = <caret>
// EXIST: { lookupString:"Foo", itemText:"Foo()" }
// EXIST: { lookupString:"Foo", itemText:"Foo", tailText: "() (<root>)" }
@@ -4,4 +4,4 @@ fun foo(p : Any){
var a : Foo<String> = <caret>
}
// EXIST: { lookupString:"Foo", itemText:"Foo<String>()" }
// EXIST: { lookupString:"Foo", itemText:"Foo<String>", tailText:"() (<root>)" }
@@ -2,4 +2,4 @@ import java.util.Date
var a : Date = <caret>
// EXIST: { lookupString:"Date", itemText:"Date(...)" }
// EXIST: { lookupString:"Date", itemText:"Date", tailText:"(...) (java.util)" }
@@ -4,4 +4,4 @@ fun foo(p : Any){
var a : Foo? = <caret>
}
// EXIST: { lookupString:"Foo", itemText:"Foo()" }
// EXIST: { lookupString:"Foo", itemText:"Foo", tailText: "() (<root>)" }
@@ -2,4 +2,4 @@ class Foo(val p1: String, val p2: Any?)
var a : Foo = <caret>
// EXIST: { lookupString:"Foo", itemText:"Foo(p1: String, p2: Any?)" }
// EXIST: { lookupString:"Foo", itemText:"Foo", tailText:"(p1: String, p2: Any?) (<root>)" }
@@ -26,4 +26,4 @@ fun f3() : String{}
// EXIST: f1
// EXIST: f2
// ABSENT: f3
// EXIST: { lookupString:"Foo", itemText:"Foo()" }
// EXIST: { lookupString:"Foo", itemText:"Foo", tailText: "() (<root>)" }
@@ -7,7 +7,7 @@ fun bar() {
fun f1(){}
fun f2(i: Int){}
// EXIST: { lookupString:"::f1", itemText:"::f1", tailText:"", typeText:"" }
// EXIST: { lookupString:"::f1", itemText:"::f1", tailText:null, typeText:null }
// ABSENT: ::f2
// ABSENT: ::Unit
// ABSENT: ::Nothing
@@ -6,7 +6,7 @@ fun bar() {
foo1() ?: <caret>
}
// EXIST: { itemText:"foo3()" }
// ABSENT: { itemText:"foo2()" }
// EXIST: { itemText:"!! foo2()" }
// EXIST: { itemText:"?: foo2()" }
// EXIST: { itemText:"foo3" }
// ABSENT: { itemText:"foo2" }
// EXIST: { itemText:"!! foo2" }
// EXIST: { itemText:"?: foo2" }
@@ -2,6 +2,6 @@ fun foo(){
val l : java.util.Calendar = <caret>
}
// EXIST: { lookupString:"getInstance", itemText:"Calendar.getInstance()", tailText:" (java.util)", typeText:"Calendar!" }
// EXIST: { lookupString:"getInstance", itemText:"Calendar.getInstance(TimeZone!)", tailText:" (java.util)", typeText:"Calendar!" }
// EXIST: { lookupString:"getInstance", itemText:"Calendar.getInstance(TimeZone!, Locale!)", tailText:" (java.util)", typeText:"Calendar!" }
// EXIST: { lookupString:"getInstance", itemText:"Calendar.getInstance", tailText:"() (java.util)", typeText:"Calendar!" }
// EXIST: { lookupString:"getInstance", itemText:"Calendar.getInstance", tailText:"(TimeZone!) (java.util)", typeText:"Calendar!" }
// EXIST: { lookupString:"getInstance", itemText:"Calendar.getInstance", tailText:"(TimeZone!, Locale!) (java.util)", typeText:"Calendar!" }
@@ -10,4 +10,4 @@ fun foo(): X {
}
// ABSENT: f
// EXIST: { lookupString:"g", itemText:"X.g()" }
// EXIST: { lookupString:"g", itemText:"X.g", tailText:"() (<root>)" }
@@ -1,3 +1,3 @@
var a : Runnable = <caret>
// EXIST: {"lookupString":"Runnable", "tailText":"", "typeText":"Runnable", "itemText":"Runnable(function: () -> Unit)"}
// EXIST: {"lookupString":"Runnable", "itemText":"Runnable", "tailText":"(function: () -> Unit)", "typeText":"Runnable"}
@@ -20,6 +20,7 @@ import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.intellij.codeInsight.lookup.LookupElement;
@@ -75,7 +76,10 @@ public class ExpectedCompletionUtils {
if (!validKeys.contains(key)) {
throw new RuntimeException("Invalid json property '" + key + "'");
}
map.put(key, entry.getValue().getAsString());
JsonElement value = entry.getValue();
if (!(value instanceof JsonNull)) {
map.put(key, value.getAsString());
}
}
}
@@ -298,6 +298,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("HigherOrderFunction2.kt")
public void testHigherOrderFunction2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/HigherOrderFunction2.kt");
doTest(fileName);
}
@TestMetadata("ImportedEnumMembers.kt")
public void testImportedEnumMembers() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/ImportedEnumMembers.kt");
@@ -298,6 +298,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("HigherOrderFunction2.kt")
public void testHigherOrderFunction2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/HigherOrderFunction2.kt");
doTest(fileName);
}
@TestMetadata("ImportedEnumMembers.kt")
public void testImportedEnumMembers() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/ImportedEnumMembers.kt");
@@ -84,11 +84,11 @@ public class BasicCompletionHandlerTest : CompletionHandlerTestBase(){
fun testHigherOrderFunctionWithArg() = doTest(2, "filterNot", null, '\n')
fun testHigherOrderFunctionWithArgs1() = doTest(1, "foo", "foo { (String, Char) -> ... }", null, '\n')
fun testHigherOrderFunctionWithArgs1() = doTest(1, "foo", "foo", " { (String, Char) -> ... }", '\n')
fun testHigherOrderFunctionWithArgs2() = doTest(1, "foo", "foo(p: (String, Char) -> Boolean)", null, '\n')
fun testHigherOrderFunctionWithArgs2() = doTest(1, "foo", "foo", "(p: (String, Char) -> Boolean)", '\n')
fun testHigherOrderFunctionWithArgs3() = doTest(1, "foo", "foo { (String, Char) -> ... }", null, '\n')
fun testHigherOrderFunctionWithArgs3() = doTest(1, "foo", "foo", " { (String, Char) -> ... }", '\n')
fun testForceParenthesisForTabChar() = doTest(0, "some", null, '\t')
@@ -161,7 +161,7 @@ public class BasicCompletionHandlerTest : CompletionHandlerTestBase(){
fun testTypeArgOfSuper() = doTest(1, "X", null, '\n')
fun testKeywordClassName() = doTest(1, "class", null, '\n')
fun testKeywordFunctionName() = doTest(1, "fun", "fun()", null, '\n')
fun testKeywordFunctionName() = doTest(1, "fun", "fun", "()", '\n')
fun testInfixCall() = doTest(1, "to", null, null, '\n')
fun testInfixCallOnSpace() = doTest(1, "to", null, null, ' ')