KT-14386 Smart completion: add "<parameter name> = true/false" choices

#KT-14386 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-10-18 18:23:52 +03:00
parent 54f28cab45
commit a2ebf07a93
14 changed files with 134 additions and 1 deletions
@@ -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<DeclarationDescriptor> by lazy<Set<DeclarationDescriptor>>(LazyThreadSafetyMode.NONE) {
val descriptorsToSkip: Set<DeclarationDescriptor> by lazy<Set<DeclarationDescriptor>> {
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<LookupElement>.addNamedArgumentsWithLiteralValueItems(expectedInfos: Collection<ExpectedInfo>) {
data class NameAndValue(val name: Name, val value: String, val priority: SmartCompletionItemPriority)
val nameAndValues = HashMap<NameAndValue, MutableList<ExpectedInfo>>()
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<ExpectedInfo> {
// if our expression is initializer of implicitly typed variable - take type of variable from original file (+ the same for function)
val declaration = implicitlyTypedDeclarationFromInitializer(expression)
@@ -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
}
@@ -0,0 +1,8 @@
fun foo(p: Int, flag: Boolean){}
fun bar() {
foo(1, <caret>)
}
// EXIST: { lookupString: "flag = true", itemText: "flag = true", attributes: "" }
// EXIST: { lookupString: "flag = false", itemText: "flag = false", attributes: "" }
@@ -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
@@ -0,0 +1,8 @@
fun foo(p: Int, flag: Boolean, x: Int){}
fun foo(p: Int, flag: Boolean?, y: Char){}
fun bar() {
foo(1, <caret>)
}
// ELEMENT_TEXT: "flag = true"
@@ -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, <caret>)
}
// ELEMENT_TEXT: "flag = true"
@@ -0,0 +1,12 @@
fun foo(p: Int, flag: Boolean){}
fun bar() {
foo(1, <caret>)
}
// 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
@@ -0,0 +1,13 @@
fun foo(s: String?, flag: Boolean, x: Int){}
fun foo(xx: Boolean){}
fun bar() {
foo(<caret>)
}
// 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: "" }
@@ -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
@@ -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
@@ -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");
@@ -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");
@@ -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");
@@ -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");