diff --git a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt index 19b64d2c976..8c4f5e6dd9a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt @@ -16,26 +16,26 @@ package org.jetbrains.kotlin.idea.parameterInfo -import com.google.common.collect.Iterables import com.intellij.codeInsight.CodeInsightBundle import com.intellij.codeInsight.lookup.LookupElement -import com.intellij.lang.ASTNode import com.intellij.lang.parameterInfo.* -import com.intellij.openapi.util.Pair import com.intellij.psi.PsiElement -import com.intellij.psi.util.PsiTreeUtil import com.intellij.ui.Gray import com.intellij.ui.JBColor import com.intellij.util.ArrayUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper import org.jetbrains.kotlin.idea.core.isVisible import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.allChildren import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils @@ -47,7 +47,7 @@ import org.jetbrains.kotlin.types.checker.JetTypeChecker import java.awt.Color import java.util.* -class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupport, JetValueArgument> { +class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupport, JetValueArgument> { override fun getActualParameters(arguments: JetValueArgumentList) = arguments.arguments.toTypedArray() @@ -65,7 +65,7 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp override fun getParametersForLookup(item: LookupElement, context: ParameterInfoContext) = emptyArray() //todo: ? - override fun getParametersForDocumentation(p: Pair, context: ParameterInfoContext) = emptyArray() //todo: ? + override fun getParametersForDocumentation(p: Pair, context: ParameterInfoContext) = emptyArray() //todo: ? override fun findElementForParameterInfo(context: CreateParameterInfoContext): JetValueArgumentList? { return findCall(context) @@ -80,22 +80,22 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp } override fun updateParameterInfo(argumentList: JetValueArgumentList, context: UpdateParameterInfoContext) { - if (context.parameterOwner !== argumentList) context.removeHint() - val offset = context.offset - var child: ASTNode? = argumentList.node.firstChildNode - var i = 0 - while (child != null && child.startOffset < offset) { - if (child.elementType === JetTokens.COMMA) ++i - child = child.treeNext + if (context.parameterOwner !== argumentList) { + context.removeHint() } - context.setCurrentParameter(i) + + val offset = context.offset + val parameterIndex = argumentList.allChildren + .takeWhile { it.startOffset < offset } + .count { it.node.elementType == JetTokens.COMMA } + context.setCurrentParameter(parameterIndex) } override fun getParameterCloseChars() = ParameterInfoUtils.DEFAULT_PARAMETER_CLOSE_CHARS override fun tracksParameterIndex() = true - override fun updateUI(itemToShow: Pair, context: ParameterInfoUIContext) { + override fun updateUI(itemToShow: Pair, context: ParameterInfoUIContext) { //todo: when we will have ability to pass Array as vararg, implement such feature here too? if (context.parameterOwner == null || !context.parameterOwner.isValid) { context.isUIComponentEnabled = false @@ -129,111 +129,119 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp isGrey = true } - val builder = StringBuilder() - val owner = context.parameterOwner val bindingContext = resolutionFacade.analyze(owner as JetElement, BodyResolveMode.FULL) - for (i in valueParameters.indices) { - if (i != 0) { - builder.append(", ") - } + val text = StringBuilder { + for (i in valueParameters.indices) { + if (i != 0) { + append(", ") + } - val highlightParameter = i == currentParameterIndex || (!namedMode && i < currentParameterIndex && Iterables.getLast(valueParameters).varargElementType != null) + val highlightParameter = i == currentParameterIndex || (!namedMode && i < currentParameterIndex && valueParameters.last().varargElementType != null) - if (highlightParameter) { - boldStartOffset = builder.length() - } + if (highlightParameter) { + boldStartOffset = length() + } - if (!namedMode) { - if (valueArguments.size() > i) { - val argument = valueArguments.get(i) - if (argument.isNamed()) { - namedMode = true + if (!namedMode) { + if (valueArguments.size() > i) { + val argument = valueArguments.get(i) + if (argument.isNamed()) { + namedMode = true + } + else { + val param = valueParameters.get(i) + append(renderParameter(param, false)) + if (i <= currentParameterIndex && !isArgumentTypeValid(bindingContext, argument, param)) { + isGrey = true + } + usedIndexes[i] = true + } } else { val param = valueParameters.get(i) - builder.append(renderParameter(param, false)) - if (i <= currentParameterIndex && !isArgumentTypeValid(bindingContext, argument, param)) { - isGrey = true - } - usedIndexes[i] = true + append(renderParameter(param, false)) } } - else { - val param = valueParameters.get(i) - builder.append(renderParameter(param, false)) - } - } - if (namedMode) { - var takeAnyArgument = true - if (valueArguments.size() > i) { - val argument = valueArguments.get(i) - if (argument.isNamed()) { - for (j in valueParameters.indices) { - val referenceExpression = argument.getArgumentName()!!.getReferenceExpression() - val param = valueParameters[j] - if (!usedIndexes[j] && param.name == referenceExpression.getReferencedNameAsName()) { - takeAnyArgument = false - usedIndexes[j] = true - builder.append(renderParameter(param, true)) - if (i < currentParameterIndex && !isArgumentTypeValid(bindingContext, argument, param)) { - isGrey = true + if (namedMode) { + var takeAnyArgument = true + if (valueArguments.size() > i) { + val argument = valueArguments.get(i) + if (argument.isNamed()) { + for (j in valueParameters.indices) { + val referenceExpression = argument.getArgumentName()!!.getReferenceExpression() + val param = valueParameters[j] + if (!usedIndexes[j] && param.name == referenceExpression.getReferencedNameAsName()) { + takeAnyArgument = false + usedIndexes[j] = true + append(renderParameter(param, true)) + if (i < currentParameterIndex && !isArgumentTypeValid(bindingContext, argument, param)) { + isGrey = true + } + break } + } + } + } + + if (takeAnyArgument) { + if (i < currentParameterIndex) { + isGrey = true + } + + for (j in valueParameters.indices) { + val param = valueParameters.get(j) + if (!usedIndexes[j]) { + usedIndexes[j] = true + append(renderParameter(param, true)) break } } } } - if (takeAnyArgument) { - if (i < currentParameterIndex) { - isGrey = true - } - - for (j in valueParameters.indices) { - val param = valueParameters.get(j) - if (!usedIndexes[j]) { - usedIndexes[j] = true - builder.append(renderParameter(param, true)) - break - } - } + if (highlightParameter) { + boldEndOffset = length() } } - if (highlightParameter) { - boldEndOffset = builder.length() + if (valueParameters.size() == 0) { + append(CodeInsightBundle.message("parameter.info.no.parameters")) } - } + }.toString() - if (valueParameters.size() == 0) { - builder.append(CodeInsightBundle.message("parameter.info.no.parameters")) - } - assert(!builder.toString().isEmpty()) { "A message about 'no parameters' or some parameters should be present: " + functionDescriptor } + assert(!text.isEmpty()) { "A message about 'no parameters' or some parameters should be present: $functionDescriptor" } - val color = if (isResolvedToDescriptor(parameterOwner, functionDescriptor, bindingContext)) GREEN_BACKGROUND else context.defaultParameterColor - context.setupUIComponentPresentation(builder.toString(), boldStartOffset, boldEndOffset, isGrey, isDeprecated, false, color) + val color = if (isResolvedToDescriptor(parameterOwner, functionDescriptor, bindingContext)) + GREEN_BACKGROUND + else + context.defaultParameterColor + + context.setupUIComponentPresentation(text, boldStartOffset, boldEndOffset, isGrey, isDeprecated, false, color) } companion object { val GREEN_BACKGROUND: Color = JBColor(Color(231, 254, 234), Gray._100) private fun renderParameter(parameter: ValueParameterDescriptor, named: Boolean): String { - val builder = StringBuilder() - if (named) builder.append("[") - if (parameter.varargElementType != null) { - builder.append("vararg ") - } - builder.append(parameter.name).append(": ").append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(getActualParameterType(parameter))) - if (parameter.hasDefaultValue()) { - val parameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(parameter) - builder.append(" = ").append(getDefaultExpressionString(parameterDeclaration)) - } - if (named) builder.append("]") - return builder.toString() + return StringBuilder { + if (named) append("[") + if (parameter.varargElementType != null) { + append("vararg ") + } + append(parameter.name) + append(": ") + append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(getActualParameterType(parameter))) + if (parameter.hasDefaultValue()) { + val parameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(parameter) + append(" = ") + append(getDefaultExpressionString(parameterDeclaration)) + } + if (named) append("]") + }.toString() } private fun getDefaultExpressionString(parameterDeclaration: PsiElement?): String { @@ -270,21 +278,18 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp private fun isIndexValid(valueParameters: List, index: Int): Boolean { // Index is within range of parameters or last parameter is vararg - return index < valueParameters.size() || (valueParameters.size() > 0 && Iterables.getLast(valueParameters).varargElementType != null) + return index < valueParameters.size() || (valueParameters.isNotEmpty() && valueParameters.last().varargElementType != null) } private fun isResolvedToDescriptor( argumentList: JetValueArgumentList, functionDescriptor: FunctionDescriptor, - bindingContext: BindingContext): Boolean { - val callNameExpression = getCallSimpleNameExpression(argumentList) + bindingContext: BindingContext + ): Boolean { + val callNameExpression = getCallNameExpression(argumentList) if (callNameExpression != null) { val declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, callNameExpression) - if (declarationDescriptor != null) { - if (declarationDescriptor === functionDescriptor) { - return true - } - } + if (declarationDescriptor === functionDescriptor) return true } return false @@ -292,22 +297,17 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp private fun findCall(context: CreateParameterInfoContext): JetValueArgumentList? { //todo: calls to this constructors, when we will have auxiliary constructors - val file = context.file - if (file !is JetFile) { - return null - } + val file = context.file as? JetFile ?: return null - val argumentList = PsiTreeUtil.getParentOfType(file.findElementAt(context.offset), JetValueArgumentList::class.java) ?: return null + val argumentList = file.findElementAt(context.offset)?.getStrictParentOfType() ?: return null - val callNameExpression = getCallSimpleNameExpression(argumentList) ?: return null + val callNameExpression = getCallNameExpression(argumentList) ?: return null val references = callNameExpression.references - if (references.size() == 0) { - return null - } + if (references.isEmpty()) return null - val resolutionFacade = callNameExpression.getContainingJetFile().getResolutionFacade() - val bindingContext = resolutionFacade.analyze(callNameExpression, BodyResolveMode.FULL) + val resolutionFacade = file.getResolutionFacade() + val bindingContext = callNameExpression.analyze(BodyResolveMode.FULL) val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, callNameExpression) val placeDescriptor = scope?.getContainingDeclaration() @@ -325,16 +325,16 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, visibilityFilter) .getReferenceVariants(callNameExpression, descriptorKindFilter, { it == refName }) - val itemsToShow = ArrayList>() + val itemsToShow = ArrayList>() for (variant in variants) { if (variant is FunctionDescriptor) { //todo: renamed functions? - itemsToShow.add(Pair.create(variant, resolutionFacade)) + itemsToShow.add(Pair(variant, resolutionFacade)) } else if (variant is ClassDescriptor) { //todo: renamed classes? for (constructorDescriptor in variant.constructors) { - itemsToShow.add(Pair.create(constructorDescriptor, resolutionFacade)) + itemsToShow.add(Pair(constructorDescriptor, resolutionFacade)) } } } @@ -343,23 +343,19 @@ class KotlinFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupp return argumentList } - private fun getCallSimpleNameExpression(argumentList: JetValueArgumentList): JetSimpleNameExpression? { - val argumentListParent = argumentList.parent - return if ((argumentListParent is JetCallElement)) - argumentListParent.getCallNameExpression() - else - null + private fun getCallNameExpression(argumentList: JetValueArgumentList): JetSimpleNameExpression? { + return (argumentList.parent as? JetCallElement)?.getCallNameExpression() } private fun findCallAndUpdateContext(context: UpdateParameterInfoContext): JetValueArgumentList? { - val file = context.file - var element = file.findElementAt(context.offset) ?: return null + var element = context.file.findElementAt(context.offset) ?: return null var parent = element.parent while (parent != null && parent !is JetValueArgumentList) { element = element!!.parent parent = parent.parent } if (parent == null) return null + val argumentList = parent as JetValueArgumentList if (element is JetValueArgument) { val i = argumentList.arguments.indexOf(element) diff --git a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractFunctionParameterInfoTest.java b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractFunctionParameterInfoTest.java index acca1961b50..7133b8d05e6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractFunctionParameterInfoTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractFunctionParameterInfoTest.java @@ -16,12 +16,12 @@ package org.jetbrains.kotlin.idea.parameterInfo; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import kotlin.Pair; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; -import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; import org.jetbrains.kotlin.idea.resolve.ResolutionFacade; +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; import org.jetbrains.kotlin.lexer.JetTokens; import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.psi.JetValueArgumentList;