KT-17165: Support array literals in annotations in completion
#KT-17165 fixed
This commit is contained in:
+73
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -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,
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
annotation class Foo(val a: Array<String> = <caret>)
|
||||
|
||||
// EXIST: "[]"
|
||||
// LANGUAGE_VERSION: 1.2
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
annotation class Bar(vararg val a: String = <caret>)
|
||||
|
||||
// EXIST: "[]"
|
||||
// LANGUAGE_VERSION: 1.2
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
annotation class Foo(val a: Array<String>)
|
||||
|
||||
@Foo(<caret>) fun foo() {}
|
||||
|
||||
// EXIST: "[]"
|
||||
// LANGUAGE_VERSION: 1.2
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
annotation class Bar(vararg val a: String)
|
||||
|
||||
@Bar(*<caret>) fun bar() {}
|
||||
|
||||
// EXIST: "[]"
|
||||
// LANGUAGE_VERSION: 1.2
|
||||
+1
@@ -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,
|
||||
|
||||
+24
@@ -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");
|
||||
|
||||
+24
@@ -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");
|
||||
|
||||
+4
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user