From 97d566105a8e862015fb59403b6a14cbb89cb24c Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Mon, 5 Oct 2015 20:28:20 +0300 Subject: [PATCH] Parameter info whole signature is built by information from resolve (+ changed policy for currentParameterIndex == 0) --- .../KotlinFunctionParameterInfoHandler.kt | 150 ++++++++---------- .../NamedAndDefaultParameter.kt | 2 +- .../functionParameterInfo/NamedParameter2.kt | 2 +- .../functionParameterInfo/NotAccessible.kt | 2 +- .../SubstituteFromParameters1.kt | 2 +- 5 files changed, 68 insertions(+), 90 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt index 1bb3be89a61..8633289c86f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt @@ -41,7 +41,6 @@ import org.jetbrains.kotlin.resolve.DelegatingBindingTrace import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.calls.CallResolver -import org.jetbrains.kotlin.resolve.calls.callUtil.allArgumentsMapped import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext @@ -138,84 +137,52 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp val valueParameters = itemToShow.valueParameters val valueArguments = argumentList.arguments - val currentParameterIndex = context.currentParameterIndex - var boldStartOffset = -1 - var boldEndOffset = -1 - - val usedIndexes = BooleanArray(valueParameters.size()) - Arrays.fill(usedIndexes, false) - - var namedMode = false - val bindingContext = argumentList.analyze(BodyResolveMode.PARTIAL) val callElement = argumentList.parent as? JetCallElement ?: return false val call = callElement.getCall(bindingContext) ?: return false - val (highlightParameterIndex, isGrey) = detectSignatureHighlighting(call, itemToShow, currentParameterIndex, bindingContext, argumentList.getResolutionFacade()) + val (argumentToParameter, highlightParameterIndex, isGrey) = matchCallWithSignature( + call, itemToShow, context.currentParameterIndex, bindingContext, argumentList.getResolutionFacade()) + val usedParameterIndices = HashSet() + var namedMode = false + var boldStartOffset = -1 + var boldEndOffset = -1 val text = StringBuilder { - for (i in valueParameters.indices) { - if (i != 0) { + fun appendParameter(parameter: ValueParameterDescriptor) { + if (length() > 0) { append(", ") } - val highlightParameter = i == highlightParameterIndex - + val highlightParameter = parameter.index == highlightParameterIndex if (highlightParameter) { boldStartOffset = length() } - if (!namedMode) { - if (valueArguments.size() > i) { - val argument = valueArguments[i] - if (argument.isNamed()) { - namedMode = true - } - else { - val param = valueParameters[i] - append(renderParameter(param, false)) - usedIndexes[i] = true - } - } - else { - val param = valueParameters[i] - append(renderParameter(param, false)) - } - } - - if (namedMode) { - var takeAnyArgument = true - if (valueArguments.size() > i) { - val argument = valueArguments[i] - if (argument.isNamed()) { - for ((j, param) in valueParameters.withIndex()) { - val referenceExpression = argument.getArgumentName()!!.getReferenceExpression() - if (!usedIndexes[j] && param.name == referenceExpression.getReferencedNameAsName()) { - takeAnyArgument = false - usedIndexes[j] = true - append(renderParameter(param, true)) - break - } - } - } - } - - if (takeAnyArgument) { - for ((j, param) in valueParameters.withIndex()) { - if (!usedIndexes[j]) { - usedIndexes[j] = true - append(renderParameter(param, true)) - break - } - } - } - } + append(renderParameter(parameter, namedMode)) if (highlightParameter) { boldEndOffset = length() } } + for (argument in valueArguments) { + val parameter = argumentToParameter(argument) ?: continue + if (!usedParameterIndices.add(parameter.index)) continue + + if (argument.isNamed()) { + namedMode = true + } + + appendParameter(parameter) + } + + for (parameter in valueParameters) { + if (parameter.index !in usedParameterIndices) { + appendParameter(parameter) + } + } + if (valueParameters.size() == 0) { append(CodeInsightBundle.message("parameter.info.no.parameters")) } @@ -324,58 +291,69 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp return argumentList } - private data class SignatureHighlighting( + private data class SignatureInfo( + val argumentToParameter: (ValueArgument) -> ValueParameterDescriptor?, val highlightParameterIndex: Int?, val isGrey: Boolean ) - private fun detectSignatureHighlighting(call: Call, overload: FunctionDescriptor, currentArgumentIndex: Int, bindingContext: BindingContext, resolutionFacade: ResolutionFacade): SignatureHighlighting { - if (currentArgumentIndex == 0) { - val highlightParameterIndex = if (overload.valueParameters.isEmpty()) null else 0 - return SignatureHighlighting(highlightParameterIndex, isGrey = false) + private fun matchCallWithSignature( + call: Call, + overload: FunctionDescriptor, + currentArgumentIndex: Int, + bindingContext: BindingContext, + resolutionFacade: ResolutionFacade + ): SignatureInfo { + if (currentArgumentIndex == 0 && call.valueArguments.isEmpty() && overload.valueParameters.isEmpty()) { + return SignatureInfo({ null }, null, isGrey = false) } assert(call.valueArguments.size() >= currentArgumentIndex) - val truncatedArguments = if (call.valueArguments.size() > currentArgumentIndex) { - call.valueArguments.subList(0, currentArgumentIndex + 1) + val argumentsBeforeCurrent = call.valueArguments.subList(0, currentArgumentIndex) + + val callToUse: Call + val currentArgument: ValueArgument + if (call.valueArguments.size() > currentArgumentIndex) { + currentArgument = call.valueArguments[currentArgumentIndex] + callToUse = call } else { - val dummyArgument = object : ValueArgument { + // add dummy current argument if we don't have one + currentArgument = object : ValueArgument { override fun getArgumentExpression(): JetExpression? = null override fun getArgumentName(): ValueArgumentName? = null override fun isNamed(): Boolean = false - override fun asElement(): JetElement = throw UnsupportedOperationException() + override fun asElement(): JetElement = call.callElement // is a hack but what to do? override fun getSpreadElement(): LeafPsiElement? = null override fun isExternal() = false } - call.valueArguments + dummyArgument + callToUse = object : DelegatingCall(call) { + val arguments = call.valueArguments + currentArgument + + override fun getValueArguments() = arguments + override fun getFunctionLiteralArguments() = emptyList() + override fun getValueArgumentList() = null + } } - //TODO: do we really need truncated call? - // leave only arguments before the current one - val truncatedCall = object : DelegatingCall(call) { - override fun getValueArguments() = truncatedArguments - override fun getFunctionLiteralArguments() = emptyList() - override fun getValueArgumentList() = null - } - - val candidates = detectCandidates(truncatedCall, bindingContext, resolutionFacade) + val candidates = detectCandidates(callToUse, bindingContext, resolutionFacade) val resolvedCall = candidates.singleOrNull { it.resultingDescriptor.original == overload.original } - ?: return SignatureHighlighting(null, isGrey = true) + ?: return SignatureInfo({ null }, null, isGrey = true) - val currentParameter = (resolvedCall.getArgumentMapping(truncatedArguments.last()) as? ArgumentMatch)?.valueParameter + val argumentToParameter = { argument: ValueArgument -> (resolvedCall.getArgumentMapping(argument) as? ArgumentMatch)?.valueParameter } + + val currentParameter = (resolvedCall.getArgumentMapping(currentArgument) as? ArgumentMatch)?.valueParameter val highlightParameterIndex = currentParameter?.index - if (!resolvedCall.allArgumentsMapped()) { // some of arguments before the current one are not mapped to any of the parameters - return SignatureHighlighting(highlightParameterIndex, isGrey = true) + if (!(argumentsBeforeCurrent + currentArgument).all { argument -> resolvedCall.getArgumentMapping(argument) is ArgumentMatch }) { // some of arguments before the current one are not mapped to any of the parameters + return SignatureInfo(argumentToParameter, highlightParameterIndex, isGrey = true) } // grey out if not all arguments before the current are matched - val isGrey = truncatedCall.valueArguments - .take(currentArgumentIndex) + val isGrey = argumentsBeforeCurrent .any { argument -> resolvedCall.getArgumentMapping(argument).isError() && !argument.hasError(bindingContext) /* ignore arguments that has error type */ } - return SignatureHighlighting(highlightParameterIndex, isGrey) + return SignatureInfo(argumentToParameter, highlightParameterIndex, isGrey) } private fun ValueArgument.hasError(bindingContext: BindingContext) diff --git a/idea/testData/parameterInfo/functionParameterInfo/NamedAndDefaultParameter.kt b/idea/testData/parameterInfo/functionParameterInfo/NamedAndDefaultParameter.kt index ea48f548cf8..7d9f4c017db 100644 --- a/idea/testData/parameterInfo/functionParameterInfo/NamedAndDefaultParameter.kt +++ b/idea/testData/parameterInfo/functionParameterInfo/NamedAndDefaultParameter.kt @@ -7,6 +7,6 @@ open class A(x: Int) { } } /* -Text: ([x: Int]), Disabled: false, Strikeout: false, Green: false Text: ([y: Boolean = true], [x: Int], [z: Long = 12345678901234], [u: String = "abc\n"], [u0: String = "" + "123"], [uu: String = "$u"], [v: Char = '\u0000'], [vv: String = "..."]), Disabled: false, Strikeout: false, Green: true +Text: ([x: Int]), Disabled: true, Strikeout: false, Green: false */ \ No newline at end of file diff --git a/idea/testData/parameterInfo/functionParameterInfo/NamedParameter2.kt b/idea/testData/parameterInfo/functionParameterInfo/NamedParameter2.kt index 1d9c1db6bc4..f2a41a0d080 100644 --- a/idea/testData/parameterInfo/functionParameterInfo/NamedParameter2.kt +++ b/idea/testData/parameterInfo/functionParameterInfo/NamedParameter2.kt @@ -7,6 +7,6 @@ open class A(x: Int) { } } /* -Text: ([x: Int]), Disabled: false, Strikeout: false, Green: false Text: ([y: Boolean], [x: Int]), Disabled: false, Strikeout: false, Green: true +Text: ([x: Int]), Disabled: true, Strikeout: false, Green: false */ \ No newline at end of file diff --git a/idea/testData/parameterInfo/functionParameterInfo/NotAccessible.kt b/idea/testData/parameterInfo/functionParameterInfo/NotAccessible.kt index 1d8afb6072b..d479deb45d2 100644 --- a/idea/testData/parameterInfo/functionParameterInfo/NotAccessible.kt +++ b/idea/testData/parameterInfo/functionParameterInfo/NotAccessible.kt @@ -7,5 +7,5 @@ fun f(c: C) { c.foo(1) } /* -Text: (), Disabled: false, Strikeout: false, Green: true +Text: (), Disabled: true, Strikeout: false, Green: true */ \ No newline at end of file diff --git a/idea/testData/parameterInfo/functionParameterInfo/SubstituteFromParameters1.kt b/idea/testData/parameterInfo/functionParameterInfo/SubstituteFromParameters1.kt index c8e4afd6cf7..53090cecd1f 100644 --- a/idea/testData/parameterInfo/functionParameterInfo/SubstituteFromParameters1.kt +++ b/idea/testData/parameterInfo/functionParameterInfo/SubstituteFromParameters1.kt @@ -5,5 +5,5 @@ fun test() { /* Text: (keyValuePair: Pair), Disabled: false, Strikeout: false, Green: true Text: (vararg values: Pair), Disabled: false, Strikeout: false, Green: false -Text: (), Disabled: false, Strikeout: false, Green: false +Text: (), Disabled: true, Strikeout: false, Green: false */ \ No newline at end of file