KT-6225 Completion of annotations for method parameters

#KT-6225 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-12-22 18:23:23 +03:00
parent 95d29e0f8a
commit 000cde3f7f
14 changed files with 288 additions and 27 deletions
@@ -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<DeclarationDescriptor> by Delegates.notNull()
fun getReferenceVariants(kindFilter: DescriptorKindFilter, shouldCastToRuntimeType: Boolean): Collection<DeclarationDescriptor> {
protected fun getReferenceVariants(kindFilter: DescriptorKindFilter, shouldCastToRuntimeType: Boolean): Collection<DeclarationDescriptor> {
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<JetModifierList>() != 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<JetModifierList>()
if (modifierList != null) {
val valueArgList = position.getStrictParentOfType<JetValueArgumentList>()
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<JetTypeReference>()
if (typeReference != null) {
val firstPartReference = PsiTreeUtil.findChildOfType(typeReference, javaClass<JetSimpleNameExpression>())
return firstPartReference == reference!!.expression
if (firstPartReference == reference.expression) {
return CompletionKind.TYPES
}
}
return false
return CompletionKind.ALL
}
private fun addReferenceVariants(kindFilter: DescriptorKindFilter, shouldCastToRuntimeType: Boolean) {
@@ -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
}
}
@@ -0,0 +1,7 @@
val v = 1
fun foo(<caret>) { }
// EXIST: inlineOptions
// ABSENT: String
// ABSENT: v
@@ -0,0 +1,3 @@
fun foo(i<caret>) { }
// EXIST: inlineOptions
@@ -0,0 +1,7 @@
val v = 1
fun foo(p: String, <caret>) { }
// EXIST: inlineOptions
// ABSENT: String
// ABSENT: v
@@ -0,0 +1,7 @@
val v = 1
fun foo(volatile <caret>) { }
// EXIST: inlineOptions
// ABSENT: String
// ABSENT: v
@@ -0,0 +1,7 @@
val v = 1
fun foo(<caret>
// EXIST: inlineOptions
// ABSENT: String
// ABSENT: v
@@ -0,0 +1,7 @@
val v = 1
fun foo(inlineOptions(InlineOp<caret>) { }
// EXIST: InlineOption
// ABSENT: String
// ABSENT: v
@@ -0,0 +1,4 @@
fun foo(koltin.<caret>) { }
// EXIST: inlineOptions
// ABSENT: String
@@ -0,0 +1,7 @@
val v = 1
fun foo([<caret>) { }
// EXIST: inlineOptions
// ABSENT: String
// ABSENT: v
@@ -0,0 +1,7 @@
val v = 1
fun foo([i<caret>) { }
// EXIST: inlineOptions
// ABSENT: String
// ABSENT: v
@@ -0,0 +1,7 @@
val v = 1
fun foo(inlineOptions(<caret>) { }
// EXIST: InlineOption
// EXIST: String
// EXIST: v
@@ -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)
@@ -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)