From f470fec840a208f5d934472fe1818f793f916041 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 3 Sep 2015 18:09:05 +0300 Subject: [PATCH] "return emptyList()" & "return emptySet()" in completion --- .../kotlin/builtins/KotlinBuiltIns.java | 21 +++++++ .../kotlin/idea/completion/CompletionUtils.kt | 59 +++++++++++++------ .../handlers/keywords/ReturnEmptyList.kt | 5 ++ .../keywords/ReturnEmptyList.kt.after | 5 ++ .../testData/keywords/ReturnCollection.kt | 9 +++ .../testData/keywords/ReturnIterable.kt | 9 +++ .../testData/keywords/ReturnList.kt | 9 +++ .../testData/keywords/ReturnSet.kt | 9 +++ .../test/KeywordCompletionTestGenerated.java | 24 ++++++++ ...KeywordCompletionHandlerTestGenerated.java | 6 ++ 10 files changed, 137 insertions(+), 19 deletions(-) create mode 100644 idea/idea-completion/testData/handlers/keywords/ReturnEmptyList.kt create mode 100644 idea/idea-completion/testData/handlers/keywords/ReturnEmptyList.kt.after create mode 100644 idea/idea-completion/testData/keywords/ReturnCollection.kt create mode 100644 idea/idea-completion/testData/keywords/ReturnIterable.kt create mode 100644 idea/idea-completion/testData/keywords/ReturnList.kt create mode 100644 idea/idea-completion/testData/keywords/ReturnSet.kt diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index df907c34b56..5590e988b70 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -169,6 +169,11 @@ public class KotlinBuiltIns { public final FqNameUnsafe _float = fqNameUnsafe("Float"); public final FqNameUnsafe _double = fqNameUnsafe("Double"); + public final FqNameUnsafe _collection = fqNameUnsafe("Collection"); + public final FqNameUnsafe _list = fqNameUnsafe("List"); + public final FqNameUnsafe _set = fqNameUnsafe("Set"); + public final FqNameUnsafe _iterable = fqNameUnsafe("Iterable"); + public final FqName data = fqName("data"); public final FqName deprecated = fqName("Deprecated"); public final FqName tailRecursive = fqName("tailrec"); @@ -1006,6 +1011,22 @@ public class KotlinBuiltIns { return type != null && isNotNullConstructedFromGivenClass(type, FQ_NAMES.string); } + public static boolean isCollectionOrNullableCollection(@NotNull JetType type) { + return isConstructedFromGivenClass(type, FQ_NAMES._collection); + } + + public static boolean isListOrNullableList(@NotNull JetType type) { + return isConstructedFromGivenClass(type, FQ_NAMES._list); + } + + public static boolean isSetOrNullableSet(@NotNull JetType type) { + return isConstructedFromGivenClass(type, FQ_NAMES._set); + } + + public static boolean isIterableOrNullableIterable(@NotNull JetType type) { + return isConstructedFromGivenClass(type, FQ_NAMES._iterable); + } + public static boolean isKClass(@NotNull ClassDescriptor descriptor) { return FQ_NAMES.kClass.equals(getFqName(descriptor)); } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt index 30b71575c1c..da9475a5591 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt @@ -39,6 +39,8 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.typeUtil.TypeNullability @@ -184,7 +186,7 @@ fun shouldCompleteThisItems(prefixMatcher: PrefixMatcher): Boolean { class ThisItemLookupObject(val receiverParameter: ReceiverParameterDescriptor, val labelName: Name?) : KeywordLookupObject() -fun ThisItemLookupObject.createLookupElement() = createKeywordWithLabelElement("this", labelName, lookupObject = this) +fun ThisItemLookupObject.createLookupElement() = createKeywordElement("this", labelName.toTail(), lookupObject = this) .withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(receiverParameter.type)) fun thisExpressionItems(bindingContext: BindingContext, position: JetExpression, prefix: String): Collection { @@ -211,7 +213,7 @@ fun returnExpressionItems(bindingContext: BindingContext, position: JetElement): if (parent is JetFunctionLiteral) { val (label, call) = parent.findLabelAndCall() if (label != null) { - result.add(createKeywordWithLabelElement("return", label, addSpace = !isUnit)) + result.add(createKeywordElementWithSpace("return", tail = label.toTail(), addSpaceAfter = !isUnit)) } // check if the current function literal is inlined and stop processing outer declarations if it's not @@ -220,14 +222,23 @@ fun returnExpressionItems(bindingContext: BindingContext, position: JetElement): } else { if (parent.hasBlockBody()) { - result.add(createKeywordWithLabelElement("return", null, addSpace = !isUnit)) + result.add(createKeywordElementWithSpace("return", addSpaceAfter = !isUnit)) - if (returnType != null && returnType.nullability() == TypeNullability.NULLABLE) { - result.add(createKeywordWithLabelElement("return null", null, addSpace = false)) - } - if (returnType != null && KotlinBuiltIns.isBooleanOrNullableBoolean(returnType)) { - result.add(createKeywordWithLabelElement("return true", null, addSpace = false)) - result.add(createKeywordWithLabelElement("return false", null, addSpace = false)) + if (returnType != null) { + if (returnType.nullability() == TypeNullability.NULLABLE) { + result.add(createKeywordElement("return null")) + } + + if (KotlinBuiltIns.isBooleanOrNullableBoolean(returnType)) { + result.add(createKeywordElement("return true")) + result.add(createKeywordElement("return false")) + } + else if (KotlinBuiltIns.isCollectionOrNullableCollection(returnType) || KotlinBuiltIns.isListOrNullableList(returnType) || KotlinBuiltIns.isIterableOrNullableIterable(returnType)) { + result.add(createKeywordElement("return", tail = " emptyList()")) + } + else if (KotlinBuiltIns.isSetOrNullableSet(returnType)) { + result.add(createKeywordElement("return", tail = " emptySet()")) + } } } break @@ -242,9 +253,16 @@ private fun JetDeclarationWithBody.returnType(bindingContext: BindingContext): J return callable.getReturnType() } -private fun createKeywordWithLabelElement(keyword: String, label: Name?, addSpace: Boolean, lookupObject: KeywordLookupObject = KeywordLookupObject()): LookupElement { - val element = createKeywordWithLabelElement(keyword, label, lookupObject) - return if (addSpace) { +private fun Name?.toTail(): String = if (this != null) "@" + render() else "" + +private fun createKeywordElementWithSpace( + keyword: String, + tail: String = "", + addSpaceAfter: Boolean = false, + lookupObject: KeywordLookupObject = KeywordLookupObject() +): LookupElement { + val element = createKeywordElement(keyword, tail, lookupObject) + return if (addSpaceAfter) { object: LookupElementDecorator(element) { override fun handleInsert(context: InsertionContext) { WithTailInsertHandler.spaceTail().handleInsert(context, getDelegate()) @@ -256,13 +274,16 @@ private fun createKeywordWithLabelElement(keyword: String, label: Name?, addSpac } } -private fun createKeywordWithLabelElement(keyword: String, label: Name?, lookupObject: KeywordLookupObject = KeywordLookupObject()): LookupElementBuilder { - val labelInCode = label?.render() - var element = LookupElementBuilder.create(lookupObject, if (label == null) keyword else "$keyword@$labelInCode") +private fun createKeywordElement( + keyword: String, + tail: String = "", + lookupObject: KeywordLookupObject = KeywordLookupObject() +): LookupElementBuilder { + var element = LookupElementBuilder.create(lookupObject, keyword + tail) element = element.withPresentableText(keyword) element = element.withBoldness(true) - if (label != null) { - element = element.withTailText("@$labelInCode", false) + if (tail.isNotEmpty()) { + element = element.withTailText(tail, false) } return element } @@ -275,12 +296,12 @@ fun breakOrContinueExpressionItems(position: JetElement, breakOrContinue: String when (parent) { is JetLoopExpression -> { if (result.isEmpty()) { - result.add(createKeywordWithLabelElement(breakOrContinue, null)) + result.add(createKeywordElement(breakOrContinue)) } val label = (parent.getParent() as? JetLabeledExpression)?.getLabelNameAsName() if (label != null) { - result.add(createKeywordWithLabelElement(breakOrContinue, label)) + result.add(createKeywordElement(breakOrContinue, tail = "@$label")) } } diff --git a/idea/idea-completion/testData/handlers/keywords/ReturnEmptyList.kt b/idea/idea-completion/testData/handlers/keywords/ReturnEmptyList.kt new file mode 100644 index 00000000000..f7dd45f4813 --- /dev/null +++ b/idea/idea-completion/testData/handlers/keywords/ReturnEmptyList.kt @@ -0,0 +1,5 @@ +fun foo(): Collection { + ret +} + +// ELEMENT: "return emptyList()" diff --git a/idea/idea-completion/testData/handlers/keywords/ReturnEmptyList.kt.after b/idea/idea-completion/testData/handlers/keywords/ReturnEmptyList.kt.after new file mode 100644 index 00000000000..48fd781c291 --- /dev/null +++ b/idea/idea-completion/testData/handlers/keywords/ReturnEmptyList.kt.after @@ -0,0 +1,5 @@ +fun foo(): Collection { + return emptyList() +} + +// ELEMENT: "return emptyList()" diff --git a/idea/idea-completion/testData/keywords/ReturnCollection.kt b/idea/idea-completion/testData/keywords/ReturnCollection.kt new file mode 100644 index 00000000000..d74a4ed9317 --- /dev/null +++ b/idea/idea-completion/testData/keywords/ReturnCollection.kt @@ -0,0 +1,9 @@ +fun foo(): Collection { + ret +} + +// INVOCATION_COUNT: 1 +// WITH_ORDER +// EXIST: { lookupString: "return", itemText: "return", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return emptyList()", itemText: "return", tailText: " emptyList()", attributes: "bold" } +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/ReturnIterable.kt b/idea/idea-completion/testData/keywords/ReturnIterable.kt new file mode 100644 index 00000000000..99a9990967f --- /dev/null +++ b/idea/idea-completion/testData/keywords/ReturnIterable.kt @@ -0,0 +1,9 @@ +fun foo(): Iterable { + ret +} + +// INVOCATION_COUNT: 1 +// WITH_ORDER +// EXIST: { lookupString: "return", itemText: "return", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return emptyList()", itemText: "return", tailText: " emptyList()", attributes: "bold" } +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/ReturnList.kt b/idea/idea-completion/testData/keywords/ReturnList.kt new file mode 100644 index 00000000000..a2ea018aa6b --- /dev/null +++ b/idea/idea-completion/testData/keywords/ReturnList.kt @@ -0,0 +1,9 @@ +fun foo(): List { + ret +} + +// INVOCATION_COUNT: 1 +// WITH_ORDER +// EXIST: { lookupString: "return", itemText: "return", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return emptyList()", itemText: "return", tailText: " emptyList()", attributes: "bold" } +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/ReturnSet.kt b/idea/idea-completion/testData/keywords/ReturnSet.kt new file mode 100644 index 00000000000..552ffbf2f00 --- /dev/null +++ b/idea/idea-completion/testData/keywords/ReturnSet.kt @@ -0,0 +1,9 @@ +fun foo(): Set { + ret +} + +// INVOCATION_COUNT: 1 +// WITH_ORDER +// EXIST: { lookupString: "return", itemText: "return", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return emptySet()", itemText: "return", tailText: " emptySet()", attributes: "bold" } +// NOTHING_ELSE diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java index 29ed701a096..4a3f3890c54 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java @@ -407,12 +407,30 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes doTest(fileName); } + @TestMetadata("ReturnCollection.kt") + public void testReturnCollection() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnCollection.kt"); + doTest(fileName); + } + + @TestMetadata("ReturnIterable.kt") + public void testReturnIterable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnIterable.kt"); + doTest(fileName); + } + @TestMetadata("ReturnKeywordName.kt") public void testReturnKeywordName() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnKeywordName.kt"); doTest(fileName); } + @TestMetadata("ReturnList.kt") + public void testReturnList() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnList.kt"); + doTest(fileName); + } + @TestMetadata("ReturnNotNull.kt") public void testReturnNotNull() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnNotNull.kt"); @@ -431,6 +449,12 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes doTest(fileName); } + @TestMetadata("ReturnSet.kt") + public void testReturnSet() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnSet.kt"); + doTest(fileName); + } + @TestMetadata("This.kt") public void testThis() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/This.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java index f8562388fbf..c0fbcd8b02c 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java @@ -83,6 +83,12 @@ public class KeywordCompletionHandlerTestGenerated extends AbstractKeywordComple doTest(fileName); } + @TestMetadata("ReturnEmptyList.kt") + public void testReturnEmptyList() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/ReturnEmptyList.kt"); + doTest(fileName); + } + @TestMetadata("ReturnInEmptyType.kt") public void testReturnInEmptyType() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/ReturnInEmptyType.kt");