Parameter info whole signature is built by information from resolve (+ changed policy for currentParameterIndex == 0)

This commit is contained in:
Valentin Kipyatkov
2015-10-05 20:28:20 +03:00
parent b33bff89a5
commit 97d566105a
5 changed files with 68 additions and 90 deletions
@@ -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<Int>()
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<FunctionLiteralArgument>()
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<FunctionLiteralArgument>()
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)
@@ -7,6 +7,6 @@ open class A(x: Int) {
}
}
/*
Text: (<highlight>[x: Int]</highlight>), Disabled: false, Strikeout: false, Green: false
Text: (<highlight>[y: Boolean = true]</highlight>, [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
*/
@@ -7,6 +7,6 @@ open class A(x: Int) {
}
}
/*
Text: (<highlight>[x: Int]</highlight>), Disabled: false, Strikeout: false, Green: false
Text: (<highlight>[y: Boolean]</highlight>, [x: Int]), Disabled: false, Strikeout: false, Green: true
Text: ([x: Int]), Disabled: true, Strikeout: false, Green: false
*/
@@ -7,5 +7,5 @@ fun f(c: C) {
c.foo(<caret>1)
}
/*
Text: (<no parameters>), Disabled: false, Strikeout: false, Green: true
Text: (<no parameters>), Disabled: true, Strikeout: false, Green: true
*/
@@ -5,5 +5,5 @@ fun test() {
/*
Text: (<highlight>keyValuePair: Pair<Int, String></highlight>), Disabled: false, Strikeout: false, Green: true
Text: (<highlight>vararg values: Pair<Int, String></highlight>), Disabled: false, Strikeout: false, Green: false
Text: (<no parameters>), Disabled: false, Strikeout: false, Green: false
Text: (<no parameters>), Disabled: true, Strikeout: false, Green: false
*/