KT-5986 No code completion for second type argument of HashMap instantiation
KT-6406 Code completion in type argument position should not include functions and variables #KT-5986 Fixed #KT-6406 Fixed
This commit is contained in:
@@ -50,6 +50,14 @@ import org.jetbrains.jet.lang.psi.psiUtil.prevLeafSkipWhitespacesAndComments
|
|||||||
import org.jetbrains.jet.lang.psi.JetValueArgument
|
import org.jetbrains.jet.lang.psi.JetValueArgument
|
||||||
import org.jetbrains.jet.lang.psi.JetValueArgumentList
|
import org.jetbrains.jet.lang.psi.JetValueArgumentList
|
||||||
import org.jetbrains.jet.lang.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.jet.lang.psi.psiUtil.getNonStrictParentOfType
|
||||||
|
import org.jetbrains.jet.lang.psi.psiUtil.prevLeaf
|
||||||
|
import org.jetbrains.jet.lang.psi.psiUtil.getParentOfType
|
||||||
|
import org.jetbrains.jet.lang.psi.JetTypeArgumentList
|
||||||
|
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||||
|
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.jet.lang.descriptors.ClassKind
|
||||||
|
import org.jetbrains.jet.plugin.caches.resolve.getResolutionFacade
|
||||||
|
|
||||||
public class KotlinCompletionContributor : CompletionContributor() {
|
public class KotlinCompletionContributor : CompletionContributor() {
|
||||||
|
|
||||||
@@ -82,7 +90,9 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
|||||||
|
|
||||||
isInFunctionLiteralParameterList(tokenBefore) -> CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED
|
isInFunctionLiteralParameterList(tokenBefore) -> CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED
|
||||||
|
|
||||||
else -> specialExtensionReceiverDummyIdentifier(tokenBefore) ?: DEFAULT_DUMMY_IDENTIFIER
|
else -> specialExtensionReceiverDummyIdentifier(tokenBefore)
|
||||||
|
?: specialInTypeArgsDummyIdentifier(tokenBefore)
|
||||||
|
?: DEFAULT_DUMMY_IDENTIFIER
|
||||||
}
|
}
|
||||||
context.setDummyIdentifier(dummyIdentifier)
|
context.setDummyIdentifier(dummyIdentifier)
|
||||||
|
|
||||||
@@ -267,4 +277,69 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun specialInTypeArgsDummyIdentifier(tokenBefore: PsiElement?): String? {
|
||||||
|
if (tokenBefore == null) return null
|
||||||
|
val pair = unclosedTypeArgListNameAndBalance(tokenBefore) ?: return null
|
||||||
|
val (nameToken, balance) = pair
|
||||||
|
assert(balance > 0)
|
||||||
|
|
||||||
|
val nameRef = nameToken.getParent() as? JetNameReferenceExpression ?: return null
|
||||||
|
val bindingContext = nameRef.getResolutionFacade().analyzeWithPartialBodyResolve(nameRef) //TODO: analyze with "resolve" mode instead of "completion"
|
||||||
|
val target = bindingContext[BindingContext.REFERENCE_TARGET, nameRef]
|
||||||
|
val targets = if (target != null) {
|
||||||
|
listOf(target)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bindingContext[BindingContext.AMBIGUOUS_REFERENCE_TARGET, nameRef] ?: return null
|
||||||
|
}
|
||||||
|
if (targets.all { it is FunctionDescriptor || it is ClassDescriptor && it.getKind() == ClassKind.CLASS }) {
|
||||||
|
return CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + ">".repeat(balance) + "$"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unclosedTypeArgListNameAndBalance(tokenBefore: PsiElement): Pair<PsiElement, Int>? {
|
||||||
|
if (tokenBefore.getParentOfType<JetTypeArgumentList>(true) != null) return null // already parsed inside type argument list
|
||||||
|
val nameToken = findCallNameTokenIfInTypeArgs(tokenBefore) ?: return null
|
||||||
|
val pair = unclosedTypeArgListNameAndBalance(nameToken)
|
||||||
|
if (pair == null) {
|
||||||
|
return Pair(nameToken, 1)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Pair(pair.first, pair.second + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val callTypeArgsTokens = TokenSet.orSet(TokenSet.create(JetTokens.IDENTIFIER, JetTokens.LT, JetTokens.GT,
|
||||||
|
JetTokens.COMMA, JetTokens.DOT, JetTokens.QUEST, JetTokens.COLON,
|
||||||
|
JetTokens.LPAR, JetTokens.RPAR, JetTokens.ARROW),
|
||||||
|
JetTokens.WHITE_SPACE_OR_COMMENT_BIT_SET)
|
||||||
|
|
||||||
|
// if the leaf could be located inside type argument list of a call (if parsed properly)
|
||||||
|
// then it returns the call name reference this type argument list would belong to
|
||||||
|
private fun findCallNameTokenIfInTypeArgs(leaf: PsiElement): PsiElement? {
|
||||||
|
var current = leaf
|
||||||
|
while (true) {
|
||||||
|
val tokenType = current.getNode()!!.getElementType()
|
||||||
|
if (tokenType !in callTypeArgsTokens) return null
|
||||||
|
|
||||||
|
if (tokenType == JetTokens.LT) {
|
||||||
|
val nameToken = current.prevLeaf(skipEmptyElements = true) ?: return null
|
||||||
|
if (nameToken.getNode()!!.getElementType() != JetTokens.IDENTIFIER) return null
|
||||||
|
return nameToken
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenType == JetTokens.GT) { // pass nested type argument list
|
||||||
|
val prev = current.prevLeaf(skipEmptyElements = true) ?: return null
|
||||||
|
val typeRef = findCallNameTokenIfInTypeArgs(prev) ?: return null
|
||||||
|
current = typeRef
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.prevLeaf(skipEmptyElements = true) ?: return null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import java.util.HashMap
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val v = HashMap<<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: String
|
||||||
|
// EXIST: java
|
||||||
|
// ABSENT: defaultBufferSize
|
||||||
|
// ABSENT: readLine
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fun foo() {
|
||||||
|
val v = listOf<<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: String
|
||||||
|
// EXIST: java
|
||||||
|
// ABSENT: defaultBufferSize
|
||||||
|
// ABSENT: readLine
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fun genericFoo<T>(p: Int){}
|
||||||
|
fun genericFoo<T>(c: Char){}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
genericFoo<<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: String
|
||||||
|
// EXIST: java
|
||||||
|
// ABSENT: defaultBufferSize
|
||||||
|
// ABSENT: readLine
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
val v = 1
|
||||||
|
fun f() = 2
|
||||||
|
|
||||||
|
object HashMap
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val v = HashMap<<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: String
|
||||||
|
// EXIST: java
|
||||||
|
// EXIST: v
|
||||||
|
// EXIST: f
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import java.util.HashMap
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val v = HashMap<List<(s: String?) -> Unit>, Set<<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: String
|
||||||
|
// EXIST: java
|
||||||
|
// ABSENT: defaultBufferSize
|
||||||
|
// ABSENT: readLine
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import java.util.HashMap
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val v = HashMap<String, <caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: String
|
||||||
|
// EXIST: java
|
||||||
@@ -33,7 +33,7 @@ import java.util.regex.Pattern;
|
|||||||
public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTest {
|
public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTest {
|
||||||
@TestMetadata("idea/testData/completion/basic/common")
|
@TestMetadata("idea/testData/completion/basic/common")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@InnerTestClasses({Common.Extensions.class, Common.NamedParameters.class, Common.Visibility.class})
|
@InnerTestClasses({Common.Extensions.class, Common.NamedParameters.class, Common.TypeArgsOrNot.class, Common.Visibility.class})
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Common extends AbstractJSBasicCompletionTest {
|
public static class Common extends AbstractJSBasicCompletionTest {
|
||||||
@TestMetadata("AfterFloatOnNewLine.kt")
|
@TestMetadata("AfterFloatOnNewLine.kt")
|
||||||
@@ -1024,6 +1024,51 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/completion/basic/common/typeArgsOrNot")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class TypeArgsOrNot extends AbstractJSBasicCompletionTest {
|
||||||
|
public void testAllFilesPresentInTypeArgsOrNot() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/completion/basic/common/typeArgsOrNot"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ConstructorTypeArg.kt")
|
||||||
|
public void testConstructorTypeArg() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/ConstructorTypeArg.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("FunctionTypeArg.kt")
|
||||||
|
public void testFunctionTypeArg() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/FunctionTypeArg.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("FunctionTypeArg2.kt")
|
||||||
|
public void testFunctionTypeArg2() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/FunctionTypeArg2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("LessThan.kt")
|
||||||
|
public void testLessThan() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/LessThan.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("NestedTypeArg.kt")
|
||||||
|
public void testNestedTypeArg() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/NestedTypeArg.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SecondTypeArg.kt")
|
||||||
|
public void testSecondTypeArg() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/SecondTypeArg.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/completion/basic/common/visibility")
|
@TestMetadata("idea/testData/completion/basic/common/visibility")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import java.util.regex.Pattern;
|
|||||||
public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionTest {
|
public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionTest {
|
||||||
@TestMetadata("idea/testData/completion/basic/common")
|
@TestMetadata("idea/testData/completion/basic/common")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@InnerTestClasses({Common.Extensions.class, Common.NamedParameters.class, Common.Visibility.class})
|
@InnerTestClasses({Common.Extensions.class, Common.NamedParameters.class, Common.TypeArgsOrNot.class, Common.Visibility.class})
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Common extends AbstractJvmBasicCompletionTest {
|
public static class Common extends AbstractJvmBasicCompletionTest {
|
||||||
@TestMetadata("AfterFloatOnNewLine.kt")
|
@TestMetadata("AfterFloatOnNewLine.kt")
|
||||||
@@ -1024,6 +1024,51 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/completion/basic/common/typeArgsOrNot")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class TypeArgsOrNot extends AbstractJvmBasicCompletionTest {
|
||||||
|
public void testAllFilesPresentInTypeArgsOrNot() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/completion/basic/common/typeArgsOrNot"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ConstructorTypeArg.kt")
|
||||||
|
public void testConstructorTypeArg() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/ConstructorTypeArg.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("FunctionTypeArg.kt")
|
||||||
|
public void testFunctionTypeArg() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/FunctionTypeArg.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("FunctionTypeArg2.kt")
|
||||||
|
public void testFunctionTypeArg2() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/FunctionTypeArg2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("LessThan.kt")
|
||||||
|
public void testLessThan() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/LessThan.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("NestedTypeArg.kt")
|
||||||
|
public void testNestedTypeArg() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/NestedTypeArg.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SecondTypeArg.kt")
|
||||||
|
public void testSecondTypeArg() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/typeArgsOrNot/SecondTypeArg.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/completion/basic/common/visibility")
|
@TestMetadata("idea/testData/completion/basic/common/visibility")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user