From a2ebf07a93e5c4c1989bfc4350315513c01d8a78 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 18 Oct 2016 18:23:52 +0300 Subject: [PATCH] KT-14386 Smart completion: add " = true/false" choices #KT-14386 Fixed --- .../idea/completion/smart/SmartCompletion.kt | 49 ++++++++++++++++++- .../kotlin/idea/completion/smart/Utils.kt | 3 ++ .../namedArguments/BooleanArgumentExpected.kt | 8 +++ .../NamedArgumentsFromOverloads2.kt | 1 + .../handlers/smart/NamedBooleanArgument.kt | 8 +++ .../smart/NamedBooleanArgument.kt.after | 8 +++ .../testData/smart/BooleanArgumentExpected.kt | 12 +++++ .../BooleanOrNullableArgumentExpected.kt | 13 +++++ .../ConcreteKClassExpectedNoDuplicates.kt | 1 + .../weighers/smart/BooleanExpected.kt | 2 + .../test/JSBasicCompletionTestGenerated.java | 6 +++ .../test/JvmBasicCompletionTestGenerated.java | 6 +++ .../test/JvmSmartCompletionTestGenerated.java | 12 +++++ .../SmartCompletionHandlerTestGenerated.java | 6 +++ 14 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 idea/idea-completion/testData/basic/common/namedArguments/BooleanArgumentExpected.kt create mode 100644 idea/idea-completion/testData/handlers/smart/NamedBooleanArgument.kt create mode 100644 idea/idea-completion/testData/handlers/smart/NamedBooleanArgument.kt.after create mode 100644 idea/idea-completion/testData/smart/BooleanArgumentExpected.kt create mode 100644 idea/idea-completion/testData/smart/BooleanOrNullableArgumentExpected.kt diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index 3cede1cd5b7..776d786d8bf 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -24,6 +24,7 @@ import com.intellij.openapi.progress.ProgressManager import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.SmartList import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.completion.* import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler @@ -33,12 +34,15 @@ import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.idea.util.isAlmostEverything import org.jetbrains.kotlin.idea.util.toFuzzyType import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.check @@ -107,7 +111,7 @@ class SmartCompletion( return postProcessedItems to postProcessedSearcher } - val descriptorsToSkip: Set by lazy>(LazyThreadSafetyMode.NONE) { + val descriptorsToSkip: Set by lazy> { val parent = expressionWithType.parent when (parent) { is KtBinaryExpression -> { @@ -216,6 +220,8 @@ class SmartCompletion( ClassLiteralItems.addToCollection(items, expectedInfos, lookupElementFactory.basicFactory, isJvmModule) + items.addNamedArgumentsWithLiteralValueItems(expectedInfos) + if (!forBasicCompletion) { LambdaItems.addToCollection(items, expectedInfos) @@ -287,6 +293,47 @@ class SmartCompletion( } } + private fun MutableCollection.addNamedArgumentsWithLiteralValueItems(expectedInfos: Collection) { + data class NameAndValue(val name: Name, val value: String, val priority: SmartCompletionItemPriority) + + val nameAndValues = HashMap>() + + fun addNameAndValue(name: Name, value: String, priority: SmartCompletionItemPriority, expectedInfo: ExpectedInfo) { + nameAndValues.getOrPut(NameAndValue(name, value, priority)) { ArrayList() }.add(expectedInfo) + } + + for (expectedInfo in expectedInfos) { + val argumentData = expectedInfo.additionalData as? ArgumentPositionData.Positional ?: continue + if (argumentData.namedArgumentCandidates.isEmpty()) continue + val parameters = argumentData.function.valueParameters + if (argumentData.argumentIndex >= parameters.size) continue + val parameterName = parameters[argumentData.argumentIndex].name + + if (expectedInfo.fuzzyType?.type?.isBooleanOrNullableBoolean() == true) { + addNameAndValue(parameterName, "true", SmartCompletionItemPriority.NAMED_ARGUMENT_TRUE, expectedInfo) + addNameAndValue(parameterName, "false", SmartCompletionItemPriority.NAMED_ARGUMENT_FALSE, expectedInfo) + } + if (expectedInfo.fuzzyType?.type?.isMarkedNullable == true) { + addNameAndValue(parameterName, "null", SmartCompletionItemPriority.NAMED_ARGUMENT_NULL, expectedInfo) + } + } + + for ((nameAndValue, infos) in nameAndValues) { + var lookupElement = createNamedArgumentWithValueLookupElement(nameAndValue.name, nameAndValue.value, nameAndValue.priority) + lookupElement = lookupElement.addTail(mergeTails(infos.map { it.tail })) + add(lookupElement) + } + } + + private fun createNamedArgumentWithValueLookupElement(name: Name, value: String, priority: SmartCompletionItemPriority): LookupElement { + val lookupElement = LookupElementBuilder.create("${name.asString()} = $value") + .withIcon(KotlinIcons.PARAMETER) + .withInsertHandler({ context, item -> context.document.replaceString(context.startOffset, context.tailOffset, "${name.render()} = $value") }) + lookupElement.putUserData(SmartCompletionInBasicWeigher.NAMED_ARGUMENT_KEY, Unit) + lookupElement.assignSmartCompletionPriority(priority) + return lookupElement + } + private fun calcExpectedInfos(expression: KtExpression): Collection { // if our expression is initializer of implicitly typed variable - take type of variable from original file (+ the same for function) val declaration = implicitlyTypedDeclarationFromInitializer(expression) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index f6d155e70fe..27462c150c5 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -266,6 +266,8 @@ enum class SmartCompletionItemPriority { IT, TRUE, FALSE, + NAMED_ARGUMENT_TRUE, + NAMED_ARGUMENT_FALSE, CLASS_LITERAL, THIS, DELEGATES_STATIC_MEMBER, @@ -278,6 +280,7 @@ enum class SmartCompletionItemPriority { LAMBDA, CALLABLE_REFERENCE, NULL, + NAMED_ARGUMENT_NULL, INHERITOR_INSTANTIATION } diff --git a/idea/idea-completion/testData/basic/common/namedArguments/BooleanArgumentExpected.kt b/idea/idea-completion/testData/basic/common/namedArguments/BooleanArgumentExpected.kt new file mode 100644 index 00000000000..2a4c644dc9b --- /dev/null +++ b/idea/idea-completion/testData/basic/common/namedArguments/BooleanArgumentExpected.kt @@ -0,0 +1,8 @@ +fun foo(p: Int, flag: Boolean){} + +fun bar() { + foo(1, ) +} + +// EXIST: { lookupString: "flag = true", itemText: "flag = true", attributes: "" } +// EXIST: { lookupString: "flag = false", itemText: "flag = false", attributes: "" } diff --git a/idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentsFromOverloads2.kt b/idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentsFromOverloads2.kt index ac185171844..3d46cf02041 100644 --- a/idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentsFromOverloads2.kt +++ b/idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentsFromOverloads2.kt @@ -10,4 +10,5 @@ fun f() { // EXIST: { itemText:"xxP1 =", tailText: " String" } // EXIST: { itemText:"xxP2 =", tailText: " ..." } // EXIST: { itemText:"xxx =", tailText: " Any?" } +// EXIST: { itemText:"xxx = null", tailText: null } // NOTHING_ELSE diff --git a/idea/idea-completion/testData/handlers/smart/NamedBooleanArgument.kt b/idea/idea-completion/testData/handlers/smart/NamedBooleanArgument.kt new file mode 100644 index 00000000000..6789ce1e291 --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/NamedBooleanArgument.kt @@ -0,0 +1,8 @@ +fun foo(p: Int, flag: Boolean, x: Int){} +fun foo(p: Int, flag: Boolean?, y: Char){} + +fun bar() { + foo(1, ) +} + +// ELEMENT_TEXT: "flag = true" diff --git a/idea/idea-completion/testData/handlers/smart/NamedBooleanArgument.kt.after b/idea/idea-completion/testData/handlers/smart/NamedBooleanArgument.kt.after new file mode 100644 index 00000000000..bd3c95e8747 --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/NamedBooleanArgument.kt.after @@ -0,0 +1,8 @@ +fun foo(p: Int, flag: Boolean, x: Int){} +fun foo(p: Int, flag: Boolean?, y: Char){} + +fun bar() { + foo(1, flag = true, ) +} + +// ELEMENT_TEXT: "flag = true" diff --git a/idea/idea-completion/testData/smart/BooleanArgumentExpected.kt b/idea/idea-completion/testData/smart/BooleanArgumentExpected.kt new file mode 100644 index 00000000000..0b756dcc737 --- /dev/null +++ b/idea/idea-completion/testData/smart/BooleanArgumentExpected.kt @@ -0,0 +1,12 @@ +fun foo(p: Int, flag: Boolean){} + +fun bar() { + foo(1, ) +} + +// WITH_ORDER +// EXIST: { itemText: "true", attributes: "bold" } +// EXIST: { itemText: "false", attributes: "bold" } +// EXIST: { lookupString: "flag = true", itemText: "flag = true", attributes: "" } +// EXIST: { lookupString: "flag = false", itemText: "flag = false", attributes: "" } +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/smart/BooleanOrNullableArgumentExpected.kt b/idea/idea-completion/testData/smart/BooleanOrNullableArgumentExpected.kt new file mode 100644 index 00000000000..2f04eeb01a1 --- /dev/null +++ b/idea/idea-completion/testData/smart/BooleanOrNullableArgumentExpected.kt @@ -0,0 +1,13 @@ +fun foo(s: String?, flag: Boolean, x: Int){} +fun foo(xx: Boolean){} + +fun bar() { + foo() +} + +// EXIST: { itemText: "null", attributes: "bold" } +// EXIST: { itemText: "true", attributes: "bold" } +// EXIST: { itemText: "false", attributes: "bold" } +// EXIST: { lookupString: "s = null", itemText: "s = null", attributes: "" } +// EXIST: { lookupString: "xx = true", itemText: "xx = true", attributes: "" } +// EXIST: { lookupString: "xx = false", itemText: "xx = false", attributes: "" } diff --git a/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpectedNoDuplicates.kt b/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpectedNoDuplicates.kt index 05710c346a9..69e4e40c3a2 100644 --- a/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpectedNoDuplicates.kt +++ b/idea/idea-completion/testData/smart/callableReference/ConcreteKClassExpectedNoDuplicates.kt @@ -11,4 +11,5 @@ fun f() { // EXIST: { lookupString: "Int", itemText: "Int::class", attributes: "" } // EXIST: { lookupString: "object" } // EXIST: null +// EXIST: { lookupString: "kClass = null", itemText: "kClass = null" } // NOTHING_ELSE diff --git a/idea/idea-completion/testData/weighers/smart/BooleanExpected.kt b/idea/idea-completion/testData/weighers/smart/BooleanExpected.kt index 9067d79bd72..ff4b62efc27 100644 --- a/idea/idea-completion/testData/weighers/smart/BooleanExpected.kt +++ b/idea/idea-completion/testData/weighers/smart/BooleanExpected.kt @@ -14,6 +14,8 @@ fun foo(pFoo: Boolean, s: String) { // ORDER: "pFoo, s" // ORDER: true // ORDER: false +// ORDER: "pFoo = true" +// ORDER: "pFoo = false" // ORDER: local // ORDER: nonNullable // ORDER: nullableX diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index 28fb6f3dffb..da7bc44ed30 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -1809,6 +1809,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/namedArguments"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("BooleanArgumentExpected.kt") + public void testBooleanArgumentExpected() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/namedArguments/BooleanArgumentExpected.kt"); + doTest(fileName); + } + @TestMetadata("CompactTypeNames.kt") public void testCompactTypeNames() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/namedArguments/CompactTypeNames.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index 23490f4f6dc..db13a31ec48 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -1809,6 +1809,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/namedArguments"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("BooleanArgumentExpected.kt") + public void testBooleanArgumentExpected() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/namedArguments/BooleanArgumentExpected.kt"); + doTest(fileName); + } + @TestMetadata("CompactTypeNames.kt") public void testCompactTypeNames() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/namedArguments/CompactTypeNames.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java index 284d810b3da..39bb44fcf17 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java @@ -83,12 +83,24 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT doTest(fileName); } + @TestMetadata("BooleanArgumentExpected.kt") + public void testBooleanArgumentExpected() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/BooleanArgumentExpected.kt"); + doTest(fileName); + } + @TestMetadata("BooleanExpected.kt") public void testBooleanExpected() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/BooleanExpected.kt"); doTest(fileName); } + @TestMetadata("BooleanOrNullableArgumentExpected.kt") + public void testBooleanOrNullableArgumentExpected() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/BooleanOrNullableArgumentExpected.kt"); + doTest(fileName); + } + @TestMetadata("ChainedCall.kt") public void testChainedCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/ChainedCall.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java index 56610b25c0c..5e50aaef7e0 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java @@ -635,6 +635,12 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion doTest(fileName); } + @TestMetadata("NamedBooleanArgument.kt") + public void testNamedBooleanArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NamedBooleanArgument.kt"); + doTest(fileName); + } + @TestMetadata("NestedDataClass.kt") public void testNestedDataClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/NestedDataClass.kt");