From 4972dbfc2dcc1e01a1234560d53bd87a3d7f705f Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Wed, 11 Oct 2017 21:23:47 +0300 Subject: [PATCH] KT-17165: Support array literals in annotations in completion #KT-17165 fixed --- .../smart/ArrayLiteralsInAnnotationItems.kt | 73 +++++++++++++++++++ .../idea/completion/smart/SmartCompletion.kt | 10 +++ .../kotlin/idea/completion/smart/Utils.kt | 1 + ...tationConstructorAsDefaultValueForArray.kt | 4 + ...ationConstructorAsDefaultValueForVararg.kt | 4 + .../ArrayLiteralAnnotationUseForArray.kt | 6 ++ .../ArrayLiteralAnnotationUseForVararg.kt | 6 ++ .../test/ExpectedCompletionUtils.kt | 1 + .../test/JSBasicCompletionTestGenerated.java | 24 ++++++ .../test/JvmBasicCompletionTestGenerated.java | 24 ++++++ .../KotlinFixtureCompletionBaseTestCase.kt | 4 + 11 files changed, 157 insertions(+) create mode 100644 idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ArrayLiteralsInAnnotationItems.kt create mode 100644 idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForArray.kt create mode 100644 idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForVararg.kt create mode 100644 idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForArray.kt create mode 100644 idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForVararg.kt diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ArrayLiteralsInAnnotationItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ArrayLiteralsInAnnotationItems.kt new file mode 100644 index 00000000000..a224adaf5ae --- /dev/null +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ArrayLiteralsInAnnotationItems.kt @@ -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.addForUsage(expectedInfos: Collection, + position: PsiElement) { + if (position.getParentOfType(false) != null) { + expectedInfos.asSequence() + .filter { it.fuzzyType?.type?.let { type -> KotlinBuiltIns.isArray(type) } == true } + .filterNot { it.itemOptions.starPrefix } + .mapTo(this) { createLookupElement() } + } + } + + private fun MutableCollection.addForDefaultArguments(expectedInfos: Collection, + 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(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, position: PsiElement): Collection { + return mutableListOf().apply { + addForUsage(expectedInfos, position) + addForDefaultArguments(expectedInfos, position) + } + } +} \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index d1f8148c835..ba93269b7d5 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -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.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") diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index bb53d87cd41..00483bf3905 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -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, diff --git a/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForArray.kt b/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForArray.kt new file mode 100644 index 00000000000..ca3e6fbd0e7 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForArray.kt @@ -0,0 +1,4 @@ +annotation class Foo(val a: Array = ) + +// EXIST: "[]" +// LANGUAGE_VERSION: 1.2 \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForVararg.kt b/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForVararg.kt new file mode 100644 index 00000000000..8659ca83eee --- /dev/null +++ b/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationConstructorAsDefaultValueForVararg.kt @@ -0,0 +1,4 @@ +annotation class Bar(vararg val a: String = ) + +// EXIST: "[]" +// LANGUAGE_VERSION: 1.2 \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForArray.kt b/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForArray.kt new file mode 100644 index 00000000000..f93688c390a --- /dev/null +++ b/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForArray.kt @@ -0,0 +1,6 @@ +annotation class Foo(val a: Array) + +@Foo() fun foo() {} + +// EXIST: "[]" +// LANGUAGE_VERSION: 1.2 \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForVararg.kt b/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForVararg.kt new file mode 100644 index 00000000000..6894717fa81 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/fromSmart/ArrayLiteralAnnotationUseForVararg.kt @@ -0,0 +1,6 @@ +annotation class Bar(vararg val a: String) + +@Bar(*) fun bar() {} + +// EXIST: "[]" +// LANGUAGE_VERSION: 1.2 \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/ExpectedCompletionUtils.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/ExpectedCompletionUtils.kt index 3da166bf9e3..9947bddbbf0 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/ExpectedCompletionUtils.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/ExpectedCompletionUtils.kt @@ -119,6 +119,7 @@ object ExpectedCompletionUtils { private val COMPLETION_TYPE_PREFIX = "COMPLETION_TYPE:" val KNOWN_PREFIXES: List = ImmutableList.of( + "LANGUAGE_VERSION:", EXIST_LINE_PREFIX, ABSENT_LINE_PREFIX, ABSENT_JS_LINE_PREFIX, diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index e1e27db5686..d1dcd39b3e4 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -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"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index 9908bba50ae..c179f0cafc3 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -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"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KotlinFixtureCompletionBaseTestCase.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KotlinFixtureCompletionBaseTestCase.kt index 8c5c854e505..5c976e30aa6 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KotlinFixtureCompletionBaseTestCase.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KotlinFixtureCompletionBaseTestCase.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("\"\" is missing in file \"$testPath\"", fileText.contains("")); if (ExpectedCompletionUtils.shouldRunHighlightingBeforeCompletion(fileText)) {