KT-35648 RemoveArgumentNameIntention: Support MixedNamedArgumentsInTheirOwnPosition
- ^KT-35648 Fixed
This commit is contained in:
committed by
Roman Golyshev
parent
401baa35ab
commit
2daedc98b5
+16
-10
@@ -34,17 +34,11 @@ import java.util.*
|
||||
object NamedArgumentCompletion {
|
||||
fun isOnlyNamedArgumentExpected(nameExpression: KtSimpleNameExpression, resolutionFacade: ResolutionFacade): Boolean {
|
||||
val thisArgument = nameExpression.parent as? KtValueArgument ?: return false
|
||||
|
||||
if (thisArgument.isNamed()) return false
|
||||
val resolvedCall = thisArgument.getStrictParentOfType<KtCallElement>()?.resolveToCall(resolutionFacade) ?: return false
|
||||
|
||||
val callElement = thisArgument.getStrictParentOfType<KtCallElement>() ?: return false
|
||||
val argumentsBeforeThis = callElement.valueArguments.takeWhile { it != thisArgument }
|
||||
|
||||
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) }
|
||||
return !thisArgument.canBeUsedWithoutNameInCall(resolvedCall)
|
||||
}
|
||||
|
||||
fun complete(collector: LookupElementsCollector, expectedInfos: Collection<ExpectedInfo>, callType: CallType<*>) {
|
||||
@@ -83,6 +77,18 @@ object NamedArgumentCompletion {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether argument in the [resolvedCall] can be used without its name (as positional argument).
|
||||
*/
|
||||
fun KtValueArgument.canBeUsedWithoutNameInCall(resolvedCall: ResolvedCall<out CallableDescriptor>): Boolean {
|
||||
val argumentsBeforeThis = resolvedCall.call.valueArguments.takeWhile { it != this }
|
||||
return if (languageVersionSettings.supportsFeature(LanguageFeature.MixedNamedArgumentsInTheirOwnPosition)) {
|
||||
argumentsBeforeThis.none { it.isNamed() && !it.placedOnItsOwnPositionInCall(resolvedCall) }
|
||||
} else {
|
||||
argumentsBeforeThis.none { it.isNamed() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether argument in the [resolvedCall] is on the same position as it listed in the callable definition.
|
||||
*
|
||||
@@ -99,6 +105,6 @@ object NamedArgumentCompletion {
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
private fun ValueArgument.placedOnItsOwnPositionInCall(resolvedCall: ResolvedCall<out CallableDescriptor>): Boolean {
|
||||
fun ValueArgument.placedOnItsOwnPositionInCall(resolvedCall: ResolvedCall<out CallableDescriptor>): Boolean {
|
||||
return resolvedCall.getParameterForArgument(this)?.index == resolvedCall.call.valueArguments.indexOf(this)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
@@ -19,33 +8,32 @@ package org.jetbrains.kotlin.idea.intentions
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.completion.canBeUsedWithoutNameInCall
|
||||
import org.jetbrains.kotlin.idea.completion.placedOnItsOwnPositionInCall
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
|
||||
class RemoveArgumentNameIntention : SelfTargetingRangeIntention<KtValueArgument>(KtValueArgument::class.java, "Remove argument name") {
|
||||
override fun applicabilityRange(element: KtValueArgument): TextRange? {
|
||||
if (!element.isNamed()) return null
|
||||
val resolvedCall = element.getStrictParentOfType<KtCallElement>()?.resolveToCall() ?: return null
|
||||
|
||||
val argumentList = element.parent as? KtValueArgumentList ?: return null
|
||||
val arguments = argumentList.arguments
|
||||
if (arguments.asSequence().takeWhile { it != element }.any { it.isNamed() }) return null
|
||||
if (!element.placedOnItsOwnPositionInCall(resolvedCall)) return null
|
||||
if (!element.canBeUsedWithoutNameInCall(resolvedCall)) return null
|
||||
|
||||
val callExpr = argumentList.parent as? KtCallElement ?: return null
|
||||
val argumentMatch = callExpr.resolveToArgumentMatch(element) ?: return null
|
||||
if (argumentMatch.valueParameter.index != arguments.indexOf(element)) return null
|
||||
|
||||
val expression = element.getArgumentExpression() ?: return null
|
||||
return TextRange(element.startOffset, expression.startOffset)
|
||||
val argumentExpression = element.getArgumentExpression() ?: return null
|
||||
return TextRange(element.startOffset, argumentExpression.startOffset)
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtValueArgument, editor: Editor?) {
|
||||
val argumentExpr = element.getArgumentExpression() ?: return
|
||||
val argumentList = element.parent as? KtValueArgumentList ?: return
|
||||
val callExpr = argumentList.parent as? KtCallElement ?: return
|
||||
val resolvedCall = (argumentList.parent as? KtCallElement)?.resolveToCall() ?: return
|
||||
val psiFactory = KtPsiFactory(element)
|
||||
if (argumentExpr is KtCollectionLiteralExpression && callExpr.resolveToArgumentMatch(element)?.valueParameter?.isVararg == true) {
|
||||
if (argumentExpr is KtCollectionLiteralExpression && resolvedCall.getParameterForArgument(element)?.isVararg == true) {
|
||||
argumentExpr.getInnerExpressions()
|
||||
.map { psiFactory.createArgument(it) }
|
||||
.reversed()
|
||||
@@ -56,8 +44,4 @@ class RemoveArgumentNameIntention : SelfTargetingRangeIntention<KtValueArgument>
|
||||
element.replace(newArgument)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtCallElement.resolveToArgumentMatch(element: KtValueArgument): ArgumentMatch? {
|
||||
return resolveToCall()?.getArgumentMapping(element) as? ArgumentMatch ?: return null
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:+MixedNamedArgumentsInTheirOwnPosition
|
||||
fun foo(s: String, b: Boolean) {}
|
||||
|
||||
fun bar() {
|
||||
foo(s = "", <caret>b = true)
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:+MixedNamedArgumentsInTheirOwnPosition
|
||||
fun foo(s: String, b: Boolean) {}
|
||||
|
||||
fun bar() {
|
||||
foo(s = "", true)
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:+MixedNamedArgumentsInTheirOwnPosition
|
||||
fun foo(name1: Int, name2: Int, name3: Int) {}
|
||||
|
||||
fun usage() {
|
||||
foo(1, name2 = 2, <caret>name3 = 3)
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:+MixedNamedArgumentsInTheirOwnPosition
|
||||
fun foo(name1: Int, name2: Int, name3: Int) {}
|
||||
|
||||
fun usage() {
|
||||
foo(1, name2 = 2, 3)
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
// COMPILER_ARGUMENTS: +XXLanguage:+MixedNamedArgumentsInTheirOwnPosition
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(name1: Int, name2: Int, name3: Int) {}
|
||||
|
||||
fun usage() {
|
||||
foo(name2 = 2, <caret>name1 = 1, name3 = 3)
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
// COMPILER_ARGUMENTS: +XXLanguage:+MixedNamedArgumentsInTheirOwnPosition
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(name1: Int, name2: Int, name3: Int) {}
|
||||
|
||||
fun usage() {
|
||||
foo(name2 = 2, name1 = 1, <caret>name3 = 3)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:-MixedNamedArgumentsInTheirOwnPosition
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(s: String, b: Boolean){}
|
||||
fun foo(s: String, b: Boolean) {}
|
||||
|
||||
fun bar() {
|
||||
foo(s = "", <caret>b = true)
|
||||
|
||||
@@ -12711,6 +12711,39 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
public void testSuperClassConstructor() throws Exception {
|
||||
runTest("idea/testData/intentions/removeArgumentName/superClassConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/removeArgumentName/MixedNamedArgumentsInTheirOwnPosition")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class MixedNamedArgumentsInTheirOwnPosition extends AbstractIntentionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMixedNamedArgumentsInTheirOwnPosition() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/intentions/removeArgumentName/MixedNamedArgumentsInTheirOwnPosition"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("namedArgumentBefore.kt")
|
||||
public void testNamedArgumentBefore() throws Exception {
|
||||
runTest("idea/testData/intentions/removeArgumentName/MixedNamedArgumentsInTheirOwnPosition/namedArgumentBefore.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("namedArgumentBefore2.kt")
|
||||
public void testNamedArgumentBefore2() throws Exception {
|
||||
runTest("idea/testData/intentions/removeArgumentName/MixedNamedArgumentsInTheirOwnPosition/namedArgumentBefore2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notInPosition.kt")
|
||||
public void testNotInPosition() throws Exception {
|
||||
runTest("idea/testData/intentions/removeArgumentName/MixedNamedArgumentsInTheirOwnPosition/notInPosition.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notInPosition2.kt")
|
||||
public void testNotInPosition2() throws Exception {
|
||||
runTest("idea/testData/intentions/removeArgumentName/MixedNamedArgumentsInTheirOwnPosition/notInPosition2.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/removeBraces")
|
||||
|
||||
Reference in New Issue
Block a user