KT-4977 Smart completion should work for arguments in brackets
#KT-4977 Fixed
This commit is contained in:
@@ -61,6 +61,7 @@ import java.util.*
|
|||||||
enum class Tail {
|
enum class Tail {
|
||||||
COMMA,
|
COMMA,
|
||||||
RPARENTH,
|
RPARENTH,
|
||||||
|
RBRACKET,
|
||||||
ELSE,
|
ELSE,
|
||||||
RBRACE
|
RBRACE
|
||||||
}
|
}
|
||||||
@@ -158,6 +159,7 @@ class ExpectedInfos(
|
|||||||
public fun calculate(expressionWithType: JetExpression): Collection<ExpectedInfo> {
|
public fun calculate(expressionWithType: JetExpression): Collection<ExpectedInfo> {
|
||||||
val expectedInfos = calculateForArgument(expressionWithType)
|
val expectedInfos = calculateForArgument(expressionWithType)
|
||||||
?: calculateForFunctionLiteralArgument(expressionWithType)
|
?: calculateForFunctionLiteralArgument(expressionWithType)
|
||||||
|
?: calculateForIndexingArgument(expressionWithType)
|
||||||
?: calculateForEqAndAssignment(expressionWithType)
|
?: calculateForEqAndAssignment(expressionWithType)
|
||||||
?: calculateForIf(expressionWithType)
|
?: calculateForIf(expressionWithType)
|
||||||
?: calculateForElvis(expressionWithType)
|
?: calculateForElvis(expressionWithType)
|
||||||
@@ -189,6 +191,15 @@ class ExpectedInfos(
|
|||||||
return calculateForArgument(callExpression, literalArgument)
|
return calculateForArgument(callExpression, literalArgument)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun calculateForIndexingArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||||
|
val containerNode = expressionWithType.parent as? JetContainerNode ?: return null
|
||||||
|
val arrayAccessExpression = containerNode.parent as? JetArrayAccessExpression ?: return null
|
||||||
|
if (containerNode != arrayAccessExpression.indicesNode) return null
|
||||||
|
val call = arrayAccessExpression.getCall(bindingContext) ?: return null
|
||||||
|
val argument = call.valueArguments.firstOrNull { it.getArgumentExpression() == expressionWithType } ?: return null
|
||||||
|
return calculateForArgument(call, argument)
|
||||||
|
}
|
||||||
|
|
||||||
private fun calculateForArgument(callElement: JetCallElement, argument: ValueArgument): Collection<ExpectedInfo>? {
|
private fun calculateForArgument(callElement: JetCallElement, argument: ValueArgument): Collection<ExpectedInfo>? {
|
||||||
val call = callElement.getCall(bindingContext) ?: return null
|
val call = callElement.getCall(bindingContext) ?: return null
|
||||||
return calculateForArgument(call, argument)
|
return calculateForArgument(call, argument)
|
||||||
@@ -312,6 +323,18 @@ class ExpectedInfos(
|
|||||||
ArgumentPositionData.Positional(descriptor, argumentIndex, isFunctionLiteralArgument, namedArgumentCandidates)
|
ArgumentPositionData.Positional(descriptor, argumentIndex, isFunctionLiteralArgument, namedArgumentCandidates)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val callType = call.callType
|
||||||
|
val isArrayAccess = callType == Call.CallType.ARRAY_GET_METHOD || callType == Call.CallType.ARRAY_SET_METHOD
|
||||||
|
val rparenthTail = if (isArrayAccess) Tail.RBRACKET else Tail.RPARENTH
|
||||||
|
|
||||||
|
var parameters = descriptor.valueParameters
|
||||||
|
if (callType == Call.CallType.ARRAY_SET_METHOD) { // last parameter in set is used for value assigned
|
||||||
|
if (parameter == parameters.last()) {
|
||||||
|
parameter = null
|
||||||
|
}
|
||||||
|
parameters = parameters.dropLast(1)
|
||||||
|
}
|
||||||
|
|
||||||
if (parameter == null) {
|
if (parameter == null) {
|
||||||
if (argumentPositionData is ArgumentPositionData.Positional && argumentPositionData.namedArgumentCandidates.isNotEmpty()) {
|
if (argumentPositionData is ArgumentPositionData.Positional && argumentPositionData.namedArgumentCandidates.isNotEmpty()) {
|
||||||
add(ExpectedInfo.createForNamedArgumentExpected(argumentPositionData))
|
add(ExpectedInfo.createForNamedArgumentExpected(argumentPositionData))
|
||||||
@@ -321,19 +344,17 @@ class ExpectedInfos(
|
|||||||
|
|
||||||
val expectedName = if (descriptor.hasSynthesizedParameterNames()) null else parameter.getName().asString()
|
val expectedName = if (descriptor.hasSynthesizedParameterNames()) null else parameter.getName().asString()
|
||||||
|
|
||||||
val parameters = descriptor.valueParameters
|
|
||||||
|
|
||||||
fun needCommaForParameter(parameter: ValueParameterDescriptor): Boolean {
|
fun needCommaForParameter(parameter: ValueParameterDescriptor): Boolean {
|
||||||
if (parameter.hasDefaultValue()) return false // parameter is optional
|
if (parameter.hasDefaultValue()) return false // parameter is optional
|
||||||
if (parameter.getVarargElementType() != null) return false // vararg arguments list can be empty
|
if (parameter.getVarargElementType() != null) return false // vararg arguments list can be empty
|
||||||
// last parameter of functional type can be placed outside parenthesis:
|
// last parameter of functional type can be placed outside parenthesis:
|
||||||
if (parameter == parameters.last() && KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameter.getType())) return false
|
if (!isArrayAccess && parameter == parameters.last() && KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameter.getType())) return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
val tail = if (argumentName == null) {
|
val tail = if (argumentName == null) {
|
||||||
if (parameter == parameters.last())
|
if (parameter == parameters.last())
|
||||||
Tail.RPARENTH //TODO: support square brackets
|
rparenthTail
|
||||||
else if (parameters.dropWhile { it != parameter }.drop(1).any(::needCommaForParameter))
|
else if (parameters.dropWhile { it != parameter }.drop(1).any(::needCommaForParameter))
|
||||||
Tail.COMMA
|
Tail.COMMA
|
||||||
else
|
else
|
||||||
@@ -349,7 +370,7 @@ class ExpectedInfos(
|
|||||||
if (varargElementType != null) {
|
if (varargElementType != null) {
|
||||||
if (isFunctionLiteralArgument) return
|
if (isFunctionLiteralArgument) return
|
||||||
|
|
||||||
val varargTail = if (argumentName == null && tail == Tail.RPARENTH)
|
val varargTail = if (argumentName == null && tail == rparenthTail)
|
||||||
null /* even if it's the last parameter, there can be more arguments for the same parameter */
|
null /* even if it's the last parameter, there can be more arguments for the same parameter */
|
||||||
else
|
else
|
||||||
tail
|
tail
|
||||||
@@ -391,7 +412,7 @@ class ExpectedInfos(
|
|||||||
val usedParameterNames = (argumentToParameter.values().map { it.getName() } + listOf(argumentName)).toSet()
|
val usedParameterNames = (argumentToParameter.values().map { it.getName() } + listOf(argumentName)).toSet()
|
||||||
val notUsedParameters = descriptor.getValueParameters().filter { it.getName() !in usedParameterNames }
|
val notUsedParameters = descriptor.getValueParameters().filter { it.getName() !in usedParameterNames }
|
||||||
return if (notUsedParameters.isEmpty())
|
return if (notUsedParameters.isEmpty())
|
||||||
Tail.RPARENTH
|
Tail.RPARENTH // named arguments no supported for []
|
||||||
else if (notUsedParameters.all { it.hasDefaultValue() })
|
else if (notUsedParameters.all { it.hasDefaultValue() })
|
||||||
null
|
null
|
||||||
else
|
else
|
||||||
|
|||||||
+1
@@ -88,6 +88,7 @@ class WithTailInsertHandler(val tailText: String,
|
|||||||
companion object {
|
companion object {
|
||||||
fun commaTail() = WithTailInsertHandler(",", spaceBefore = false, spaceAfter = true /*TODO: use code style option*/)
|
fun commaTail() = WithTailInsertHandler(",", spaceBefore = false, spaceAfter = true /*TODO: use code style option*/)
|
||||||
fun rparenthTail() = WithTailInsertHandler(")", spaceBefore = false, spaceAfter = false)
|
fun rparenthTail() = WithTailInsertHandler(")", spaceBefore = false, spaceAfter = false)
|
||||||
|
fun rbracketTail() = WithTailInsertHandler("]", spaceBefore = false, spaceAfter = false)
|
||||||
fun rbraceTail() = WithTailInsertHandler("}", spaceBefore = true, spaceAfter = false)
|
fun rbraceTail() = WithTailInsertHandler("}", spaceBefore = true, spaceAfter = false)
|
||||||
fun elseTail() = WithTailInsertHandler("else", spaceBefore = true, spaceAfter = true)
|
fun elseTail() = WithTailInsertHandler("else", spaceBefore = true, spaceAfter = true)
|
||||||
fun eqTail() = WithTailInsertHandler("=", spaceBefore = true, spaceAfter = true) /*TODO: use code style options*/
|
fun eqTail() = WithTailInsertHandler("=", spaceBefore = true, spaceAfter = true) /*TODO: use code style options*/
|
||||||
|
|||||||
@@ -75,6 +75,12 @@ fun LookupElement.addTail(tail: Tail?): LookupElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tail.RBRACKET -> object: LookupElementDecorator<LookupElement>(this) {
|
||||||
|
override fun handleInsert(context: InsertionContext) {
|
||||||
|
WithTailInsertHandler.rbracketTail().handleInsert(context, getDelegate())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Tail.ELSE -> object: LookupElementDecorator<LookupElement>(this) {
|
Tail.ELSE -> object: LookupElementDecorator<LookupElement>(this) {
|
||||||
override fun handleInsert(context: InsertionContext) {
|
override fun handleInsert(context: InsertionContext) {
|
||||||
WithTailInsertHandler.elseTail().handleInsert(context, getDelegate())
|
WithTailInsertHandler.elseTail().handleInsert(context, getDelegate())
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(map: Map<String, Int>, p: String) {
|
||||||
|
map[<caret>]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELEMENT: p
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(map: Map<String, Int>, p: String) {
|
||||||
|
map[p]<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELEMENT: p
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(map: Map<String, Int>, p: String) {
|
||||||
|
map[<caret>] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELEMENT: p
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(map: Map<String, Int>, p: String) {
|
||||||
|
map[p]<caret> = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELEMENT: p
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
fun foo(map: Map<String, Int>, p1: Any, p2: String) {
|
||||||
|
map[<caret>]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABSENT: p1
|
||||||
|
// EXIST: p2
|
||||||
+6
@@ -1012,6 +1012,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("GetWithBrackets.kt")
|
||||||
|
public void testGetWithBrackets() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/heuristicSignatures/GetWithBrackets.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("InOperator.kt")
|
@TestMetadata("InOperator.kt")
|
||||||
public void testInOperator() throws Exception {
|
public void testInOperator() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/heuristicSignatures/InOperator.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/heuristicSignatures/InOperator.kt");
|
||||||
|
|||||||
+12
@@ -389,6 +389,12 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("GetWithBrackets.kt")
|
||||||
|
public void testGetWithBrackets() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/GetWithBrackets.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("IfCondition.kt")
|
@TestMetadata("IfCondition.kt")
|
||||||
public void testIfCondition() throws Exception {
|
public void testIfCondition() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/IfCondition.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/IfCondition.kt");
|
||||||
@@ -695,6 +701,12 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SetWithBrackets.kt")
|
||||||
|
public void testSetWithBrackets() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/SetWithBrackets.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("TabReplaceComma1.kt")
|
@TestMetadata("TabReplaceComma1.kt")
|
||||||
public void testTabReplaceComma1() throws Exception {
|
public void testTabReplaceComma1() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/TabReplaceComma1.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/TabReplaceComma1.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user