diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt
index 9bedc061bfa..f617185be69 100644
--- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt
+++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt
@@ -52,6 +52,7 @@ fun KotlinType.isNothing(): Boolean = KotlinBuiltIns.isNothing(this)
fun KotlinType.isNullableNothing(): Boolean = KotlinBuiltIns.isNullableNothing(this)
fun KotlinType.isUnit(): Boolean = KotlinBuiltIns.isUnit(this)
fun KotlinType.isAnyOrNullableAny(): Boolean = KotlinBuiltIns.isAnyOrNullableAny(this)
+fun KotlinType.isNullableAny(): Boolean = KotlinBuiltIns.isNullableAny(this)
fun KotlinType.isBoolean(): Boolean = KotlinBuiltIns.isBoolean(this)
fun KotlinType.isBooleanOrNullableBoolean(): Boolean = KotlinBuiltIns.isBooleanOrNullableBoolean(this)
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 7d31a9851ae..481e08c7fb0 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -364,6 +364,8 @@
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/editor/KotlinTypedHandler.java b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinTypedHandler.java
index 4449a093eae..a1f962cb7e4 100644
--- a/idea/src/org/jetbrains/kotlin/idea/editor/KotlinTypedHandler.java
+++ b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinTypedHandler.java
@@ -75,6 +75,7 @@ public class KotlinTypedHandler extends TypedHandlerDelegate {
case '<':
kotlinLTTyped = CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET &&
LtGtTypingUtils.shouldAutoCloseAngleBracket(editor.getCaretModel().getOffset(), editor);
+ autoPopupParameterInfo(project, editor);
return Result.CONTINUE;
case '>':
diff --git a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinTypeArgumentInfoHandler.kt b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinTypeArgumentInfoHandler.kt
new file mode 100644
index 00000000000..e11cedc53dc
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinTypeArgumentInfoHandler.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2010-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.parameterInfo
+
+import com.intellij.codeInsight.lookup.LookupElement
+import com.intellij.lang.parameterInfo.*
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.analyze
+import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
+import org.jetbrains.kotlin.idea.core.resolveCandidates
+import org.jetbrains.kotlin.idea.references.mainReference
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.allChildren
+import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
+import org.jetbrains.kotlin.psi.psiUtil.parents
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+import org.jetbrains.kotlin.renderer.DescriptorRenderer
+import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
+import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
+import org.jetbrains.kotlin.types.Variance
+import org.jetbrains.kotlin.types.typeUtil.TypeNullability
+import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
+import org.jetbrains.kotlin.types.typeUtil.nullability
+
+class KotlinClassTypeArgumentInfoHandler : KotlinTypeArgumentInfoHandlerBase() {
+ override fun fetchTypeParameters(descriptor: ClassDescriptor) = descriptor.typeConstructor.parameters
+
+ override fun findParameterOwners(argumentList: KtTypeArgumentList): Collection? {
+ val userType = argumentList.parent as? KtUserType ?: return null
+ val bindingContext = argumentList.analyze(BodyResolveMode.PARTIAL)
+ return userType.referenceExpression?.mainReference?.resolveToDescriptors(bindingContext)?.mapNotNull { it as? ClassDescriptor }
+ }
+
+ override fun getArgumentListAllowedParentClasses() = setOf(KtUserType::class.java)
+}
+
+class KotlinFunctionTypeArgumentInfoHandler : KotlinTypeArgumentInfoHandlerBase() {
+ override fun fetchTypeParameters(descriptor: FunctionDescriptor) = descriptor.typeParameters
+
+ override fun findParameterOwners(argumentList: KtTypeArgumentList): Collection? {
+ val callElement = argumentList.parent as? KtCallElement ?: return null
+ val bindingContext = argumentList.analyze(BodyResolveMode.PARTIAL)
+ val call = callElement.getCall(bindingContext) ?: return null
+ val candidates = call.resolveCandidates(bindingContext, callElement.getResolutionFacade())
+ return candidates
+ .map { it.resultingDescriptor }
+ .distinctBy { buildPresentation(it.typeParameters, -1).first }
+ }
+
+ override fun getArgumentListAllowedParentClasses() = setOf(KtCallElement::class.java)
+}
+
+abstract class KotlinTypeArgumentInfoHandlerBase : ParameterInfoHandlerWithTabActionSupport {
+ protected abstract fun fetchTypeParameters(descriptor: TParameterOwner): List
+ protected abstract fun findParameterOwners(argumentList: KtTypeArgumentList): Collection?
+
+ override fun getActualParameterDelimiterType() = KtTokens.COMMA
+ override fun getActualParametersRBraceType() = KtTokens.GT
+
+ override fun getArgumentListClass() = KtTypeArgumentList::class.java
+
+ override fun getActualParameters(o: KtTypeArgumentList) = o.arguments.toTypedArray()
+
+ override fun getArgListStopSearchClasses() = setOf(KtNamedFunction::class.java, KtVariableDeclaration::class.java, KtClassOrObject::class.java)
+
+ override fun getParameterCloseChars() = ParameterInfoUtils.DEFAULT_PARAMETER_CLOSE_CHARS
+
+ override fun tracksParameterIndex() = true
+
+ override fun couldShowInLookup() = false
+ override fun getParametersForLookup(item: LookupElement?, context: ParameterInfoContext?) = emptyArray()
+ override fun getParametersForDocumentation(p: TParameterOwner, context: ParameterInfoContext?) = emptyArray()
+
+ override fun showParameterInfo(element: KtTypeArgumentList, context: CreateParameterInfoContext) {
+ context.showHint(element, element.textRange.startOffset, this)
+ }
+
+ override fun findElementForUpdatingParameterInfo(context: UpdateParameterInfoContext): KtTypeArgumentList? {
+ val element = context.file.findElementAt(context.offset) ?: return null
+ val argumentList = element.getParentOfType(true) ?: return null
+ val argument = element.parents.takeWhile { it != argumentList }.lastOrNull() as? KtTypeProjection
+ if (argument != null) {
+ val arguments = getActualParameters(argumentList)
+ val index = arguments.indexOf(element)
+ context.setCurrentParameter(index)
+ context.setHighlightedParameter(element)
+ }
+ return argumentList
+ }
+
+ override fun findElementForParameterInfo(context: CreateParameterInfoContext): KtTypeArgumentList? {
+ val file = context.file as? KtFile ?: return null
+
+ val token = file.findElementAt(context.offset) ?: return null
+ val argumentList = token.getParentOfType(true) ?: return null
+
+ val parameterOwners = findParameterOwners(argumentList) ?: return null
+
+ context.itemsToShow = parameterOwners.toTypedArray()
+ return argumentList
+ }
+
+ override fun updateParameterInfo(argumentList: KtTypeArgumentList, context: UpdateParameterInfoContext) {
+ if (context.parameterOwner !== argumentList) {
+ context.removeHint()
+ }
+
+ val offset = context.offset
+ val parameterIndex = argumentList.allChildren
+ .takeWhile { it.startOffset < offset }
+ .count { it.node.elementType == KtTokens.COMMA }
+ context.setCurrentParameter(parameterIndex)
+ }
+
+ override fun updateUI(itemToShow: TParameterOwner, context: ParameterInfoUIContext) {
+ if (!updateUIOrFail(itemToShow, context)) {
+ context.isUIComponentEnabled = false
+ return
+ }
+ }
+
+ private fun updateUIOrFail(itemToShow: TParameterOwner, context: ParameterInfoUIContext): Boolean {
+ val currentIndex = context.currentParameterIndex
+ if (currentIndex < 0) return false // by some strange reason we are invoked with currentParameterIndex == -1 during initialization
+
+ val parameters = fetchTypeParameters(itemToShow)
+
+ val (text, currentParameterStart, currentParameterEnd) = buildPresentation(parameters, currentIndex)
+
+ context.setupUIComponentPresentation(text, currentParameterStart, currentParameterEnd,
+ false/*isDisabled*/, false/*strikeout*/, false/*isDisabledBeforeHighlight*/,
+ context.defaultParameterColor)
+
+ return true
+ }
+
+ protected fun buildPresentation(
+ parameters: List,
+ currentIndex: Int
+ ): Triple {
+ var currentParameterStart = -1
+ var currentParameterEnd = -1
+
+ val text = buildString {
+ var needWhere = false
+ for ((index, parameter) in parameters.withIndex()) {
+ if (index > 0) append(", ")
+
+ if (index == currentIndex) {
+ currentParameterStart = length
+ }
+
+ if (parameter.isReified) {
+ append("reified ")
+ }
+
+ when (parameter.variance) {
+ Variance.INVARIANT -> {}
+ Variance.IN_VARIANCE -> append("in ")
+ Variance.OUT_VARIANCE -> append("out ")
+ }
+
+ append(parameter.name.asString())
+
+ val upperBounds = parameter.upperBounds
+ if (upperBounds.size == 1) {
+ val upperBound = upperBounds.single()
+ if (!upperBound.isAnyOrNullableAny() || upperBound.nullability() == TypeNullability.NOT_NULL) { // skip Any? or Any!
+ append(" : ").append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(upperBound))
+ }
+ }
+ else if (upperBounds.size > 1) {
+ needWhere = true
+ }
+
+ if (index == currentIndex) {
+ currentParameterEnd = length
+ }
+ }
+
+ if (needWhere) {
+ append(" where ")
+
+ var needComma = false
+ for (parameter in parameters) {
+ val upperBounds = parameter.upperBounds
+ if (upperBounds.size > 1) {
+ for (upperBound in upperBounds) {
+ if (needComma) append(", ")
+ needComma = true
+ append(parameter.name.asString()).append(" : ").append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(upperBound))
+ }
+ }
+ }
+ }
+ }
+ return Triple(text, currentParameterStart, currentParameterEnd)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/parameterInfo/typeArguments/BaseClass.kt b/idea/testData/parameterInfo/typeArguments/BaseClass.kt
new file mode 100644
index 00000000000..86d4a801107
--- /dev/null
+++ b/idea/testData/parameterInfo/typeArguments/BaseClass.kt
@@ -0,0 +1,4 @@
+open class Base
+
+class X : Base<>
+//Text: (out T, in V), Disabled: false, Strikeout: false, Green: false
\ No newline at end of file
diff --git a/idea/testData/parameterInfo/typeArguments/Constraints.kt b/idea/testData/parameterInfo/typeArguments/Constraints.kt
new file mode 100644
index 00000000000..3cb9589297f
--- /dev/null
+++ b/idea/testData/parameterInfo/typeArguments/Constraints.kt
@@ -0,0 +1,5 @@
+open class A> where T2 : Runnable, T2 : Cloneable, T3 : Runnable
+
+val v: A>
+
+//Text: (T1 : Any, T2, T3 where T2 : Runnable, T2 : Cloneable, T3 : List, T3 : Runnable), Disabled: false, Strikeout: false, Green: false
diff --git a/idea/testData/parameterInfo/typeArguments/ConstructorCall.kt b/idea/testData/parameterInfo/typeArguments/ConstructorCall.kt
new file mode 100644
index 00000000000..ff36dadba0e
--- /dev/null
+++ b/idea/testData/parameterInfo/typeArguments/ConstructorCall.kt
@@ -0,0 +1,5 @@
+open class Base
+
+val v = Base<>
+
+//Text: (out T, in V), Disabled: false, Strikeout: false, Green: false
\ No newline at end of file
diff --git a/idea/testData/parameterInfo/typeArguments/FunctionCall.kt b/idea/testData/parameterInfo/typeArguments/FunctionCall.kt
new file mode 100644
index 00000000000..a175b300b68
--- /dev/null
+++ b/idea/testData/parameterInfo/typeArguments/FunctionCall.kt
@@ -0,0 +1,7 @@
+fun foo(t: T): T = t
+
+fun bar() {
+ foo<>()
+}
+
+//Text: (T, V), Disabled: false, Strikeout: false, Green: false
diff --git a/idea/testData/parameterInfo/typeArguments/JavaClass.kt b/idea/testData/parameterInfo/typeArguments/JavaClass.kt
new file mode 100644
index 00000000000..dd3293cd377
--- /dev/null
+++ b/idea/testData/parameterInfo/typeArguments/JavaClass.kt
@@ -0,0 +1,7 @@
+import java.util.*
+
+fun foo() {
+ ArrayList<>
+}
+
+//Text: (E), Disabled: false, Strikeout: false, Green: false
\ No newline at end of file
diff --git a/idea/testData/parameterInfo/typeArguments/Overloads.kt b/idea/testData/parameterInfo/typeArguments/Overloads.kt
new file mode 100644
index 00000000000..c6bf7ecbd53
--- /dev/null
+++ b/idea/testData/parameterInfo/typeArguments/Overloads.kt
@@ -0,0 +1,12 @@
+fun foo(t: T): T = t
+fun foo(t: T, p: Int): T = t
+fun foo(p: Boolean, x: X) {}
+
+fun bar() {
+ foo<>()
+}
+
+/*
+Text: (T, V), Disabled: false, Strikeout: false, Green: false
+Text: (X), Disabled: false, Strikeout: false, Green: false
+*/
\ No newline at end of file
diff --git a/idea/testData/parameterInfo/typeArguments/Reified.kt b/idea/testData/parameterInfo/typeArguments/Reified.kt
new file mode 100644
index 00000000000..e38f280ecf9
--- /dev/null
+++ b/idea/testData/parameterInfo/typeArguments/Reified.kt
@@ -0,0 +1,7 @@
+inline fun foo(p: T) {}
+
+fun bar() {
+ foo<>
+}
+
+//Text: (reified T), Disabled: false, Strikeout: false, Green: false
diff --git a/idea/testData/parameterInfo/typeArguments/VariableType.kt b/idea/testData/parameterInfo/typeArguments/VariableType.kt
new file mode 100644
index 00000000000..20ea0f443ec
--- /dev/null
+++ b/idea/testData/parameterInfo/typeArguments/VariableType.kt
@@ -0,0 +1,5 @@
+open class A
+
+val v: A>
+
+//Text: (T, V), Disabled: false, Strikeout: false, Green: false
diff --git a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt
index be2bff367f2..0a91b5b23d4 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt
+++ b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt
@@ -22,7 +22,6 @@ import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
-import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.test.ProjectDescriptorWithStdlibSources
@@ -77,8 +76,7 @@ abstract class AbstractParameterInfoTest : LightCodeInsightFixtureTestCase() {
val parameterInfoUIContext = MockParameterInfoUIContext(parameterOwner, updateContext.currentParameter)
for (item in mockCreateParameterInfoContext.itemsToShow) {
- //noinspection unchecked
- handler.updateUI(item as FunctionDescriptor, parameterInfoUIContext)
+ handler.updateUI(item, parameterInfoUIContext)
}
Assert.assertEquals(expectedResultText, parameterInfoUIContext.resultText)
}
diff --git a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/ParameterInfoTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/ParameterInfoTestGenerated.java
index ac587e516af..7cd934fb0f7 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/ParameterInfoTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/ParameterInfoTestGenerated.java
@@ -304,4 +304,61 @@ public class ParameterInfoTestGenerated extends AbstractParameterInfoTest {
doTest(fileName);
}
}
+
+ @TestMetadata("idea/testData/parameterInfo/typeArguments")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class TypeArguments extends AbstractParameterInfoTest {
+ public void testAllFilesPresentInTypeArguments() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/parameterInfo/typeArguments"), Pattern.compile("^(.+)\\.kt$"), true);
+ }
+
+ @TestMetadata("BaseClass.kt")
+ public void testBaseClass() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/typeArguments/BaseClass.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("Constraints.kt")
+ public void testConstraints() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/typeArguments/Constraints.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("ConstructorCall.kt")
+ public void testConstructorCall() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/typeArguments/ConstructorCall.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FunctionCall.kt")
+ public void testFunctionCall() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/typeArguments/FunctionCall.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("JavaClass.kt")
+ public void testJavaClass() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/typeArguments/JavaClass.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("Overloads.kt")
+ public void testOverloads() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/typeArguments/Overloads.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("Reified.kt")
+ public void testReified() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/typeArguments/Reified.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("VariableType.kt")
+ public void testVariableType() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/parameterInfo/typeArguments/VariableType.kt");
+ doTest(fileName);
+ }
+ }
}