KT-35258 Add better completion for MixedNamedArgumentsInTheirOwnPosition feature
- When named parameter is on its own position, the completion of the next argument will not be limited by named arguments only - The completion will not suggest already used arguments names - ^KT-35258 Fixed
This commit is contained in:
committed by
Roman Golyshev
parent
bd713db70e
commit
4b50fb18fb
+1
-1
@@ -105,7 +105,7 @@ class BasicCompletionSession(
|
||||
if (OPERATOR_NAME.isApplicable())
|
||||
return OPERATOR_NAME
|
||||
|
||||
if (NamedArgumentCompletion.isOnlyNamedArgumentExpected(nameExpression)) {
|
||||
if (NamedArgumentCompletion.isOnlyNamedArgumentExpected(nameExpression, resolutionFacade)) {
|
||||
return NAMED_ARGUMENTS_ONLY
|
||||
}
|
||||
|
||||
|
||||
+36
-2
@@ -9,28 +9,42 @@ import com.intellij.codeInsight.completion.InsertHandler
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
|
||||
import org.jetbrains.kotlin.idea.core.ArgumentPositionData
|
||||
import org.jetbrains.kotlin.idea.core.ExpectedInfo
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtCallElement
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.psi.KtValueArgument
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.renderer.render
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
object NamedArgumentCompletion {
|
||||
fun isOnlyNamedArgumentExpected(nameExpression: KtSimpleNameExpression): Boolean {
|
||||
fun isOnlyNamedArgumentExpected(nameExpression: KtSimpleNameExpression, resolutionFacade: ResolutionFacade): Boolean {
|
||||
val thisArgument = nameExpression.parent as? KtValueArgument ?: return false
|
||||
if (thisArgument.isNamed()) return false
|
||||
|
||||
val callElement = thisArgument.getStrictParentOfType<KtCallElement>() ?: return false
|
||||
val argumentsBeforeThis = callElement.valueArguments.takeWhile { it != thisArgument }
|
||||
|
||||
return callElement.valueArguments.takeWhile { it != thisArgument }.any { it.isNamed() }
|
||||
if (!nameExpression.languageVersionSettings.supportsFeature(LanguageFeature.MixedNamedArgumentsInTheirOwnPosition)) {
|
||||
return argumentsBeforeThis.any { it.isNamed() }
|
||||
}
|
||||
|
||||
val resolvedCall = callElement.resolveToCall(resolutionFacade) ?: return false
|
||||
return argumentsBeforeThis.any { it.isNamed() && !it.placedOnItsOwnPositionInCall(resolvedCall) }
|
||||
}
|
||||
|
||||
fun complete(collector: LookupElementsCollector, expectedInfos: Collection<ExpectedInfo>, callType: CallType<*>) {
|
||||
@@ -68,3 +82,23 @@ object NamedArgumentCompletion {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether argument in the [resolvedCall] is on the same position as it listed in the callable definition.
|
||||
*
|
||||
* It is always true for the positional arguments, but may be untrue for the named arguments.
|
||||
*
|
||||
* ```
|
||||
* fun foo(a: Int, b: Int, c: Int, d: Int) {}
|
||||
*
|
||||
* foo(
|
||||
* 10, // true
|
||||
* b = 10, // true, possible since Kotlin 1.4 with `MixedNamedArgumentsInTheirOwnPosition` feature
|
||||
* d = 30, // false, 3 vs 4
|
||||
* c = 40 // false, 4 vs 3
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
private fun ValueArgument.placedOnItsOwnPositionInCall(resolvedCall: ResolvedCall<out CallableDescriptor>): Boolean {
|
||||
return resolvedCall.getParameterForArgument(this)?.index == resolvedCall.call.valueArguments.indexOf(this)
|
||||
}
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class SmartCompletionSession(
|
||||
get() = smartCompletion?.expectedInfos ?: emptyList()
|
||||
|
||||
override fun doComplete() {
|
||||
if (nameExpression != null && NamedArgumentCompletion.isOnlyNamedArgumentExpected(nameExpression)) {
|
||||
if (nameExpression != null && NamedArgumentCompletion.isOnlyNamedArgumentExpected(nameExpression, resolutionFacade)) {
|
||||
NamedArgumentCompletion.complete(collector, expectedInfos, callTypeAndReceiver.callType)
|
||||
return
|
||||
}
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
fun foo(p1: Int, p2: Int, p3: Long) {}
|
||||
|
||||
fun usage(param: Long) {
|
||||
foo(p2 = 10, <caret>)
|
||||
}
|
||||
|
||||
// LANGUAGE_VERSION: 1.4
|
||||
// EXIST: { itemText: "p1 =" }
|
||||
// EXIST: { itemText: "p3 =" }
|
||||
// NOTHING_ELSE
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fun foo(p1: Int, p2: Int, p3: Long) {}
|
||||
|
||||
fun usage(param: Long) {
|
||||
foo(p1 = 10, <caret>)
|
||||
}
|
||||
|
||||
// LANGUAGE_VERSION: 1.4
|
||||
// EXIST: param
|
||||
// EXIST: { itemText: "p2 =" }
|
||||
// EXIST: { itemText: "p3 =" }
|
||||
// ABSENT: { itemText: "p1 =" }
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
fun foo(p1: Int, p2: Int, p3: Int, p4: Int, p5: Int) {}
|
||||
|
||||
fun usage(param: Int) {
|
||||
foo(0, p2 = 10, 20, p4 = 30, <caret>)
|
||||
}
|
||||
|
||||
// LANGUAGE_VERSION: 1.4
|
||||
// EXIST: param
|
||||
// EXIST: { itemText: "p5 =" }
|
||||
// ABSENT: { itemText: "p1 =" }
|
||||
// ABSENT: { itemText: "p2 =" }
|
||||
// ABSENT: { itemText: "p3 =" }
|
||||
// ABSENT: { itemText: "p4 =" }
|
||||
+15
@@ -1799,6 +1799,21 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/InParameterExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NamedArgumentOnIncorrectPosition.kt")
|
||||
public void testNamedArgumentOnIncorrectPosition() throws Exception {
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentOnIncorrectPosition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NamedArgumentOnItsOwnPosition.kt")
|
||||
public void testNamedArgumentOnItsOwnPosition() throws Exception {
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentOnItsOwnPosition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NamedArgumentOnItsOwnPosition2.kt")
|
||||
public void testNamedArgumentOnItsOwnPosition2() throws Exception {
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentOnItsOwnPosition2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NamedArgumentsFromOverloads.kt")
|
||||
public void testNamedArgumentsFromOverloads() throws Exception {
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentsFromOverloads.kt");
|
||||
|
||||
+15
@@ -1799,6 +1799,21 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/InParameterExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NamedArgumentOnIncorrectPosition.kt")
|
||||
public void testNamedArgumentOnIncorrectPosition() throws Exception {
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentOnIncorrectPosition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NamedArgumentOnItsOwnPosition.kt")
|
||||
public void testNamedArgumentOnItsOwnPosition() throws Exception {
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentOnItsOwnPosition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NamedArgumentOnItsOwnPosition2.kt")
|
||||
public void testNamedArgumentOnItsOwnPosition2() throws Exception {
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentOnItsOwnPosition2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NamedArgumentsFromOverloads.kt")
|
||||
public void testNamedArgumentsFromOverloads() throws Exception {
|
||||
runTest("idea/idea-completion/testData/basic/common/namedArguments/NamedArgumentsFromOverloads.kt");
|
||||
|
||||
@@ -62,6 +62,10 @@ fun Call.mapArgumentsToParameters(targetDescriptor: CallableDescriptor): Map<Val
|
||||
val parameter = parametersByName[argumentName]
|
||||
if (parameter != null) {
|
||||
map[argument] = parameter
|
||||
if (parameter.index == positionalArgumentIndex) {
|
||||
positionalArgumentIndex++
|
||||
continue
|
||||
}
|
||||
}
|
||||
positionalArgumentIndex = null
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user