diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index 3345c0f2274..01a5c09c435 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -46,6 +46,7 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import com.intellij.patterns.StandardPatterns import com.intellij.util.ProcessingContext import com.intellij.patterns.PatternCondition +import org.jetbrains.jet.lang.psi.psiUtil.isAncestor class CompletionSessionConfiguration( val completeNonImportedDeclarations: Boolean, @@ -168,7 +169,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess // set is used only for completion in code fragments private var alreadyAddedDescriptors: Collection by Delegates.notNull() - fun getReferenceVariants(kindFilter: DescriptorKindFilter, shouldCastToRuntimeType: Boolean): Collection { + protected fun getReferenceVariants(kindFilter: DescriptorKindFilter, shouldCastToRuntimeType: Boolean): Collection { val descriptors = referenceVariantsHelper!!.getReferenceVariants(reference!!.expression, kindFilter, shouldCastToRuntimeType, prefixMatcher.asNameFilter()) if (!shouldCastToRuntimeType) { if (position.getContainingFile() is JetCodeFragment) { @@ -218,16 +219,21 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, override fun doComplete() { assert(parameters.getCompletionType() == CompletionType.BASIC) - if (!NamedParametersCompletion.isOnlyNamedParameterExpected(position)) { - val completeReference = reference != null && !isOnlyKeywordCompletion() - val onlyTypes = completeReference && shouldRunOnlyTypeCompletion() + val completionKind = completionKind() - val kindFilter = if (onlyTypes) - DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) exclude DescriptorKindExclude.EnumEntry - else - DescriptorKindFilter(DescriptorKindFilter.ALL_KINDS_MASK) + if (completionKind != CompletionKind.NAMED_PARAMETERS_ONLY) { + val kindFilter = when (completionKind) { + CompletionKind.TYPES -> + DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) exclude DescriptorKindExclude.EnumEntry - if (completeReference) { + CompletionKind.ANNOTATION_TYPES -> + DescriptorKindFilter(DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) exclude NonAnnotationClassifierExclude + + else -> + DescriptorKindFilter(DescriptorKindFilter.ALL_KINDS_MASK) + } + + if (completionKind != CompletionKind.KEYWORDS_ONLY) { addReferenceVariants(kindFilter, shouldCastToRuntimeType = false) } @@ -260,51 +266,84 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, } } - if (completeReference) { + if (completionKind != CompletionKind.KEYWORDS_ONLY) { if (!configuration.completeNonImportedDeclarations && isNoQualifierContext()) { JavaCompletionContributor.advertiseSecondCompletion(project, resultSet) } flushToResultSet() - addNonImported(onlyTypes) - } + addNonImported(completionKind) - if (completeReference && position.getContainingFile() is JetCodeFragment) { - flushToResultSet() - addReferenceVariants(kindFilter, shouldCastToRuntimeType = true) + if (position.getContainingFile() is JetCodeFragment) { + flushToResultSet() + addReferenceVariants(kindFilter, shouldCastToRuntimeType = true) + } } } NamedParametersCompletion.complete(position, collector) } - private fun addNonImported(onlyTypes: Boolean) { - if (shouldRunTopLevelCompletion()) { - addAllClasses { it != ClassKind.ENUM_ENTRY } + private object NonAnnotationClassifierExclude : DescriptorKindExclude { + override fun matches(descriptor: DeclarationDescriptor): Boolean { + return if (descriptor is ClassDescriptor) + descriptor.getKind() != ClassKind.ANNOTATION_CLASS + else + descriptor !is ClassifierDescriptor + } + } - if (!onlyTypes) { + private fun addNonImported(completionKind: CompletionKind) { + if (shouldRunTopLevelCompletion()) { + addAllClasses { + if (completionKind != CompletionKind.ANNOTATION_TYPES) + it != ClassKind.ENUM_ENTRY + else + it == ClassKind.ANNOTATION_CLASS + } + + if (completionKind == CompletionKind.ALL) { collector.addDescriptorElements(getKotlinTopLevelCallables(), suppressAutoInsertion = true) } } - if (!onlyTypes && shouldRunExtensionsCompletion()) { + if (completionKind == CompletionKind.ALL && shouldRunExtensionsCompletion()) { collector.addDescriptorElements(getKotlinExtensions(), suppressAutoInsertion = true) } } - private fun isOnlyKeywordCompletion() - = position.getStrictParentOfType() != null + private enum class CompletionKind { + KEYWORDS_ONLY + NAMED_PARAMETERS_ONLY + ALL + TYPES + ANNOTATION_TYPES + } + + private fun completionKind(): CompletionKind { + if (NamedParametersCompletion.isOnlyNamedParameterExpected(position)) return CompletionKind.NAMED_PARAMETERS_ONLY + + if (reference == null) return CompletionKind.KEYWORDS_ONLY + + val modifierList = position.getStrictParentOfType() + if (modifierList != null) { + val valueArgList = position.getStrictParentOfType() + if (valueArgList == null || !modifierList.isAncestor(valueArgList)) { + return CompletionKind.ANNOTATION_TYPES + } + } - private fun shouldRunOnlyTypeCompletion(): Boolean { // Check that completion in the type annotation context and if there's a qualified // expression we are at first of it val typeReference = position.getStrictParentOfType() if (typeReference != null) { val firstPartReference = PsiTreeUtil.findChildOfType(typeReference, javaClass()) - return firstPartReference == reference!!.expression + if (firstPartReference == reference.expression) { + return CompletionKind.TYPES + } } - return false + return CompletionKind.ALL } private fun addReferenceVariants(kindFilter: DescriptorKindFilter, shouldCastToRuntimeType: Boolean) { diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt b/idea/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt index e643394bcff..845460126e5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt @@ -59,6 +59,8 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.psi.JetTypeElement +import org.jetbrains.kotlin.psi.JetAnnotationEntry public class KotlinCompletionContributor : CompletionContributor() { @@ -93,6 +95,7 @@ public class KotlinCompletionContributor : CompletionContributor() { else -> specialExtensionReceiverDummyIdentifier(tokenBefore) ?: specialInTypeArgsDummyIdentifier(tokenBefore) + ?: specialInParameterListDummyIdentifier(tokenBefore) ?: DEFAULT_DUMMY_IDENTIFIER } context.setDummyIdentifier(dummyIdentifier) @@ -343,4 +346,34 @@ public class KotlinCompletionContributor : CompletionContributor() { current = current.prevLeaf(skipEmptyElements = true) ?: return null } } + + private fun specialInParameterListDummyIdentifier(tokenBefore: PsiElement?): String? { + if (tokenBefore == null) return null + var parent = tokenBefore.getParent() + while (parent != null) { + if (parent is JetParameterList) { + val balance = countParenthesisBalance(tokenBefore, parent) + val count = if (balance > 1) balance - 1 else 0 + return CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + ")".repeat(count) + " a: B$" + } + if (parent is JetTypeElement) return null + if (parent is JetAnnotationEntry) return null + parent = parent.getParent() + } + return null + } + + private fun countParenthesisBalance(at: PsiElement, container: PsiElement): Int { + val stopAt = container.prevLeaf() + var current: PsiElement? = at + var balance = 0 + while (current != stopAt) { + when (current!!.getNode().getElementType()) { + JetTokens.LPAR -> balance++ + JetTokens.RPAR -> balance-- + } + current = current!!.prevLeaf() + } + return balance + } } diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotation1.kt b/idea/testData/completion/basic/common/annotations/ParameterAnnotation1.kt new file mode 100644 index 00000000000..43fd1e7effe --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotation1.kt @@ -0,0 +1,7 @@ +val v = 1 + +fun foo() { } + +// EXIST: inlineOptions +// ABSENT: String +// ABSENT: v diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotation2.kt b/idea/testData/completion/basic/common/annotations/ParameterAnnotation2.kt new file mode 100644 index 00000000000..260fb160d56 --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotation2.kt @@ -0,0 +1,3 @@ +fun foo(i) { } + +// EXIST: inlineOptions diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotation3.kt b/idea/testData/completion/basic/common/annotations/ParameterAnnotation3.kt new file mode 100644 index 00000000000..30ad74e4f1f --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotation3.kt @@ -0,0 +1,7 @@ +val v = 1 + +fun foo(p: String, ) { } + +// EXIST: inlineOptions +// ABSENT: String +// ABSENT: v diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotation4.kt b/idea/testData/completion/basic/common/annotations/ParameterAnnotation4.kt new file mode 100644 index 00000000000..54bf47aa6c9 --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotation4.kt @@ -0,0 +1,7 @@ +val v = 1 + +fun foo(volatile ) { } + +// EXIST: inlineOptions +// ABSENT: String +// ABSENT: v diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotation5.kt b/idea/testData/completion/basic/common/annotations/ParameterAnnotation5.kt new file mode 100644 index 00000000000..e7e54fb0325 --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotation5.kt @@ -0,0 +1,7 @@ +val v = 1 + +fun foo( + +// EXIST: inlineOptions +// ABSENT: String +// ABSENT: v diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotation6.kt b/idea/testData/completion/basic/common/annotations/ParameterAnnotation6.kt new file mode 100644 index 00000000000..4698e6ee538 --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotation6.kt @@ -0,0 +1,7 @@ +val v = 1 + +fun foo(inlineOptions(InlineOp) { } + +// EXIST: InlineOption +// ABSENT: String +// ABSENT: v diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotation7.kt.todo b/idea/testData/completion/basic/common/annotations/ParameterAnnotation7.kt.todo new file mode 100644 index 00000000000..f4fef0c786c --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotation7.kt.todo @@ -0,0 +1,4 @@ +fun foo(koltin.) { } + +// EXIST: inlineOptions +// ABSENT: String diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotation8.kt b/idea/testData/completion/basic/common/annotations/ParameterAnnotation8.kt new file mode 100644 index 00000000000..d1e6ee65ecb --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotation8.kt @@ -0,0 +1,7 @@ +val v = 1 + +fun foo([) { } + +// EXIST: inlineOptions +// ABSENT: String +// ABSENT: v diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotation9.kt b/idea/testData/completion/basic/common/annotations/ParameterAnnotation9.kt new file mode 100644 index 00000000000..031752bfc32 --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotation9.kt @@ -0,0 +1,7 @@ +val v = 1 + +fun foo([i) { } + +// EXIST: inlineOptions +// ABSENT: String +// ABSENT: v diff --git a/idea/testData/completion/basic/common/annotations/ParameterAnnotationArgs.kt b/idea/testData/completion/basic/common/annotations/ParameterAnnotationArgs.kt new file mode 100644 index 00000000000..ee181c79585 --- /dev/null +++ b/idea/testData/completion/basic/common/annotations/ParameterAnnotationArgs.kt @@ -0,0 +1,7 @@ +val v = 1 + +fun foo(inlineOptions() { } + +// EXIST: InlineOption +// EXIST: String +// EXIST: v diff --git a/idea/tests/org/jetbrains/kotlin/completion/JSBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/completion/JSBasicCompletionTestGenerated.java index 243ab878e16..f4bb381764a 100644 --- a/idea/tests/org/jetbrains/kotlin/completion/JSBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/completion/JSBasicCompletionTestGenerated.java @@ -33,7 +33,7 @@ import java.util.regex.Pattern; public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTest { @TestMetadata("idea/testData/completion/basic/common") @TestDataPath("$PROJECT_ROOT") - @InnerTestClasses({Common.Extensions.class, Common.NamedParameters.class, Common.TypeArgsOrNot.class, Common.Visibility.class}) + @InnerTestClasses({Common.Annotations.class, Common.Extensions.class, Common.NamedParameters.class, Common.TypeArgsOrNot.class, Common.Visibility.class}) @RunWith(JUnit3RunnerWithInners.class) public static class Common extends AbstractJSBasicCompletionTest { @TestMetadata("AfterFloatOnNewLine.kt") @@ -892,6 +892,69 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes doTest(fileName); } + @TestMetadata("idea/testData/completion/basic/common/annotations") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Annotations extends AbstractJSBasicCompletionTest { + public void testAllFilesPresentInAnnotations() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/completion/basic/common/annotations"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ParameterAnnotation1.kt") + public void testParameterAnnotation1() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation1.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation2.kt") + public void testParameterAnnotation2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation2.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation3.kt") + public void testParameterAnnotation3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation3.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation4.kt") + public void testParameterAnnotation4() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation4.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation5.kt") + public void testParameterAnnotation5() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation5.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation6.kt") + public void testParameterAnnotation6() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation6.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation8.kt") + public void testParameterAnnotation8() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation8.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation9.kt") + public void testParameterAnnotation9() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation9.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotationArgs.kt") + public void testParameterAnnotationArgs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotationArgs.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/completion/basic/common/extensions") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/tests/org/jetbrains/kotlin/completion/JvmBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/completion/JvmBasicCompletionTestGenerated.java index 30a25c9ab44..7a41d05477c 100644 --- a/idea/tests/org/jetbrains/kotlin/completion/JvmBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/completion/JvmBasicCompletionTestGenerated.java @@ -33,7 +33,7 @@ import java.util.regex.Pattern; public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionTest { @TestMetadata("idea/testData/completion/basic/common") @TestDataPath("$PROJECT_ROOT") - @InnerTestClasses({Common.Extensions.class, Common.NamedParameters.class, Common.TypeArgsOrNot.class, Common.Visibility.class}) + @InnerTestClasses({Common.Annotations.class, Common.Extensions.class, Common.NamedParameters.class, Common.TypeArgsOrNot.class, Common.Visibility.class}) @RunWith(JUnit3RunnerWithInners.class) public static class Common extends AbstractJvmBasicCompletionTest { @TestMetadata("AfterFloatOnNewLine.kt") @@ -892,6 +892,69 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("idea/testData/completion/basic/common/annotations") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Annotations extends AbstractJvmBasicCompletionTest { + public void testAllFilesPresentInAnnotations() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/completion/basic/common/annotations"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ParameterAnnotation1.kt") + public void testParameterAnnotation1() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation1.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation2.kt") + public void testParameterAnnotation2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation2.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation3.kt") + public void testParameterAnnotation3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation3.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation4.kt") + public void testParameterAnnotation4() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation4.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation5.kt") + public void testParameterAnnotation5() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation5.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation6.kt") + public void testParameterAnnotation6() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation6.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation8.kt") + public void testParameterAnnotation8() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation8.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotation9.kt") + public void testParameterAnnotation9() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotation9.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterAnnotationArgs.kt") + public void testParameterAnnotationArgs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/annotations/ParameterAnnotationArgs.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/completion/basic/common/extensions") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)