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.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.CallResolver 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.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
@@ -138,84 +137,52 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp
val valueParameters = itemToShow.valueParameters val valueParameters = itemToShow.valueParameters
val valueArguments = argumentList.arguments 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 bindingContext = argumentList.analyze(BodyResolveMode.PARTIAL)
val callElement = argumentList.parent as? JetCallElement ?: return false val callElement = argumentList.parent as? JetCallElement ?: return false
val call = callElement.getCall(bindingContext) ?: 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 { val text = StringBuilder {
for (i in valueParameters.indices) { fun appendParameter(parameter: ValueParameterDescriptor) {
if (i != 0) { if (length() > 0) {
append(", ") append(", ")
} }
val highlightParameter = i == highlightParameterIndex val highlightParameter = parameter.index == highlightParameterIndex
if (highlightParameter) { if (highlightParameter) {
boldStartOffset = length() boldStartOffset = length()
} }
if (!namedMode) { append(renderParameter(parameter, 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
}
}
}
}
if (highlightParameter) { if (highlightParameter) {
boldEndOffset = length() 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) { if (valueParameters.size() == 0) {
append(CodeInsightBundle.message("parameter.info.no.parameters")) append(CodeInsightBundle.message("parameter.info.no.parameters"))
} }
@@ -324,58 +291,69 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp
return argumentList return argumentList
} }
private data class SignatureHighlighting( private data class SignatureInfo(
val argumentToParameter: (ValueArgument) -> ValueParameterDescriptor?,
val highlightParameterIndex: Int?, val highlightParameterIndex: Int?,
val isGrey: Boolean val isGrey: Boolean
) )
private fun detectSignatureHighlighting(call: Call, overload: FunctionDescriptor, currentArgumentIndex: Int, bindingContext: BindingContext, resolutionFacade: ResolutionFacade): SignatureHighlighting { private fun matchCallWithSignature(
if (currentArgumentIndex == 0) { call: Call,
val highlightParameterIndex = if (overload.valueParameters.isEmpty()) null else 0 overload: FunctionDescriptor,
return SignatureHighlighting(highlightParameterIndex, isGrey = false) 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) assert(call.valueArguments.size() >= currentArgumentIndex)
val truncatedArguments = if (call.valueArguments.size() > currentArgumentIndex) { val argumentsBeforeCurrent = call.valueArguments.subList(0, currentArgumentIndex)
call.valueArguments.subList(0, currentArgumentIndex + 1)
val callToUse: Call
val currentArgument: ValueArgument
if (call.valueArguments.size() > currentArgumentIndex) {
currentArgument = call.valueArguments[currentArgumentIndex]
callToUse = call
} }
else { 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 getArgumentExpression(): JetExpression? = null
override fun getArgumentName(): ValueArgumentName? = null override fun getArgumentName(): ValueArgumentName? = null
override fun isNamed(): Boolean = false 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 getSpreadElement(): LeafPsiElement? = null
override fun isExternal() = false 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? val candidates = detectCandidates(callToUse, bindingContext, resolutionFacade)
// 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 resolvedCall = candidates.singleOrNull { it.resultingDescriptor.original == overload.original } 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 val highlightParameterIndex = currentParameter?.index
if (!resolvedCall.allArgumentsMapped()) { // some of arguments before the current one are not mapped to any of the parameters 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 SignatureHighlighting(highlightParameterIndex, isGrey = true) return SignatureInfo(argumentToParameter, highlightParameterIndex, isGrey = true)
} }
// grey out if not all arguments before the current are matched // grey out if not all arguments before the current are matched
val isGrey = truncatedCall.valueArguments val isGrey = argumentsBeforeCurrent
.take(currentArgumentIndex)
.any { argument -> resolvedCall.getArgumentMapping(argument).isError() && !argument.hasError(bindingContext) /* ignore arguments that has error type */ } .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) 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: (<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: (<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) 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>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: (<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
*/ */