KT-17165: Support array literals in annotations in completion

#KT-17165 fixed
This commit is contained in:
Simon Ogorodnik
2017-10-11 21:23:47 +03:00
parent beec788bd4
commit 4972dbfc2d
11 changed files with 157 additions and 0 deletions
@@ -0,0 +1,73 @@
/*
* Copyright 2010-2017 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.completion.smart
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.idea.core.ExpectedInfo
import org.jetbrains.kotlin.idea.core.fuzzyType
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
object ArrayLiteralsInAnnotationItems {
private fun MutableCollection<LookupElement>.addForUsage(expectedInfos: Collection<ExpectedInfo>,
position: PsiElement) {
if (position.getParentOfType<KtAnnotationEntry>(false) != null) {
expectedInfos.asSequence()
.filter { it.fuzzyType?.type?.let { type -> KotlinBuiltIns.isArray(type) } == true }
.filterNot { it.itemOptions.starPrefix }
.mapTo(this) { createLookupElement() }
}
}
private fun MutableCollection<LookupElement>.addForDefaultArguments(expectedInfos: Collection<ExpectedInfo>,
position: PsiElement) {
// CLASS [MODIFIER_LIST, PRIMARY_CONSTRUCTOR [VALUE_PARAMETER_LIST [VALUE_PARAMETER [..., REFERENCE_EXPRESSION=position]]]]
val valueParameter = position.parent as? KtParameter ?: return
val klass = position.getParentOfType<KtClass>(true) ?: return
if (!klass.hasModifier(KtTokens.ANNOTATION_KEYWORD)) return
val primaryConstructor = klass.primaryConstructor ?: return
if (primaryConstructor.valueParameterList == valueParameter.parent) {
expectedInfos
.filter { it.fuzzyType?.type?.let { type -> KotlinBuiltIns.isArray(type) } == true }
.mapTo(this) { createLookupElement() }
}
}
private fun createLookupElement(): LookupElement {
return LookupElementBuilder.create("[]")
.withInsertHandler { context, _ ->
context.editor.caretModel.moveToOffset(context.tailOffset - 1)
}
.apply { putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, SmartCompletionItemPriority.ARRAY_LITERAL_IN_ANNOTATION) }
}
fun collect(expectedInfos: Collection<ExpectedInfo>, position: PsiElement): Collection<LookupElement> {
return mutableListOf<LookupElement>().apply {
addForUsage(expectedInfos, position)
addForDefaultArguments(expectedInfos, position)
}
}
}
@@ -23,12 +23,14 @@ import com.intellij.codeInsight.lookup.*
import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.SmartList
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.isAlmostEverything
@@ -202,6 +204,8 @@ class SmartCompletion(
}
if (expectedInfos.isNotEmpty()) {
items.addArrayLiteralsInAnnotationsCompletions()
if (!forBasicCompletion && (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT || callTypeAndReceiver is CallTypeAndReceiver.UNKNOWN /* after this@ */)) {
items.addThisItems(expression, expectedInfos, smartCastCalculator)
}
@@ -439,6 +443,12 @@ class SmartCompletion(
return items
}
private fun MutableCollection<LookupElement>.addArrayLiteralsInAnnotationsCompletions() {
if (expression.languageVersionSettings.supportsFeature(LanguageFeature.ArrayLiteralsInAnnotations)) {
this.addAll(ArrayLiteralsInAnnotationItems.collect(expectedInfos, expression))
}
}
companion object {
val OLD_ARGUMENTS_REPLACEMENT_OFFSET: OffsetKey = OffsetKey.create("nonFunctionReplacementOffset")
val MULTIPLE_ARGUMENTS_REPLACEMENT_OFFSET: OffsetKey = OffsetKey.create("multipleArgumentsReplacementOffset")
@@ -265,6 +265,7 @@ fun CallableDescriptor.callableReferenceType(resolutionFacade: ResolutionFacade,
}
enum class SmartCompletionItemPriority {
ARRAY_LITERAL_IN_ANNOTATION,
MULTIPLE_ARGUMENTS_ITEM,
LAMBDA_SIGNATURE,
LAMBDA_SIGNATURE_EXPLICIT_PARAMETER_TYPES,
@@ -0,0 +1,4 @@
annotation class Foo(val a: Array<String> = <caret>)
// EXIST: "[]"
// LANGUAGE_VERSION: 1.2
@@ -0,0 +1,4 @@
annotation class Bar(vararg val a: String = <caret>)
// EXIST: "[]"
// LANGUAGE_VERSION: 1.2
@@ -0,0 +1,6 @@
annotation class Foo(val a: Array<String>)
@Foo(<caret>) fun foo() {}
// EXIST: "[]"
// LANGUAGE_VERSION: 1.2
@@ -0,0 +1,6 @@
annotation class Bar(vararg val a: String)
@Bar(*<caret>) fun bar() {}
// EXIST: "[]"
// LANGUAGE_VERSION: 1.2
@@ -119,6 +119,7 @@ object ExpectedCompletionUtils {
private val COMPLETION_TYPE_PREFIX = "COMPLETION_TYPE:"
val KNOWN_PREFIXES: List<String> = ImmutableList.of(
"LANGUAGE_VERSION:",
EXIST_LINE_PREFIX,
ABSENT_LINE_PREFIX,
ABSENT_JS_LINE_PREFIX,
@@ -1561,6 +1561,30 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromSmart"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("ArrayLiteralAnnotationConstructorAsDefaultValueForArray.kt")
public void testArrayLiteralAnnotationConstructorAsDefaultValueForArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForArray.kt");
doTest(fileName);
}
@TestMetadata("ArrayLiteralAnnotationConstructorAsDefaultValueForVararg.kt")
public void testArrayLiteralAnnotationConstructorAsDefaultValueForVararg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForVararg.kt");
doTest(fileName);
}
@TestMetadata("ArrayLiteralAnnotationUseForArray.kt")
public void testArrayLiteralAnnotationUseForArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForArray.kt");
doTest(fileName);
}
@TestMetadata("ArrayLiteralAnnotationUseForVararg.kt")
public void testArrayLiteralAnnotationUseForVararg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForVararg.kt");
doTest(fileName);
}
@TestMetadata("EnumEntries.kt")
public void testEnumEntries() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/EnumEntries.kt");
@@ -1561,6 +1561,30 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromSmart"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("ArrayLiteralAnnotationConstructorAsDefaultValueForArray.kt")
public void testArrayLiteralAnnotationConstructorAsDefaultValueForArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForArray.kt");
doTest(fileName);
}
@TestMetadata("ArrayLiteralAnnotationConstructorAsDefaultValueForVararg.kt")
public void testArrayLiteralAnnotationConstructorAsDefaultValueForVararg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForVararg.kt");
doTest(fileName);
}
@TestMetadata("ArrayLiteralAnnotationUseForArray.kt")
public void testArrayLiteralAnnotationUseForArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForArray.kt");
doTest(fileName);
}
@TestMetadata("ArrayLiteralAnnotationUseForVararg.kt")
public void testArrayLiteralAnnotationUseForVararg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForVararg.kt");
doTest(fileName);
}
@TestMetadata("EnumEntries.kt")
public void testEnumEntries() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/EnumEntries.kt");
@@ -19,8 +19,10 @@ package org.jetbrains.kotlin.idea.completion.test
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.LightProjectDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.LibraryModificationTracker
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.configureLanguageVersion
import org.jetbrains.kotlin.resolve.TargetPlatform
import java.io.File
@@ -38,6 +40,8 @@ abstract class KotlinFixtureCompletionBaseTestCase : KotlinLightCodeInsightFixtu
try {
val fileText = FileUtil.loadFile(File(testPath), true)
configureLanguageVersion(fileText, project, module)
assertTrue("\"<caret>\" is missing in file \"$testPath\"", fileText.contains("<caret>"));
if (ExpectedCompletionUtils.shouldRunHighlightingBeforeCompletion(fileText)) {