KT-14386 Smart completion: add "<parameter name> = true/false" choices
#KT-14386 Fixed
This commit is contained in:
+48
-1
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -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: "" }
|
||||
+1
@@ -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: "" }
|
||||
Vendored
+1
@@ -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
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
+12
@@ -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");
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user