KT-12103 Smart completion for nested SAM-adapter produces short unresolved name
#KT-12103 Fixed
This commit is contained in:
@@ -16,7 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.*
|
||||
import com.intellij.codeInsight.completion.CompletionProgressIndicator
|
||||
import com.intellij.codeInsight.completion.CompletionService
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.completion.PrefixMatcher
|
||||
import com.intellij.codeInsight.lookup.*
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.util.Key
|
||||
@@ -359,14 +362,14 @@ fun LookupElement.decorateAsStaticMember(
|
||||
memberDescriptor: DeclarationDescriptor,
|
||||
classNameAsLookupString: Boolean
|
||||
): LookupElement? {
|
||||
var container = memberDescriptor.containingDeclaration as? ClassDescriptor ?: return null
|
||||
var classDescriptor = if (container.isCompanionObject)
|
||||
val container = memberDescriptor.containingDeclaration as? ClassDescriptor ?: return null
|
||||
val classDescriptor = if (container.isCompanionObject)
|
||||
container.containingDeclaration as? ClassDescriptor ?: return null
|
||||
else
|
||||
container
|
||||
|
||||
val containerFqName = container.importableFqName ?: return null
|
||||
val qualifierPresentation = classDescriptor.name.asString()
|
||||
val qualifierText = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classDescriptor)
|
||||
|
||||
return object: LookupElementDecorator<LookupElement>(this) {
|
||||
override fun getAllLookupStrings(): Set<String> {
|
||||
@@ -395,31 +398,14 @@ fun LookupElement.decorateAsStaticMember(
|
||||
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
|
||||
val file = context.file as KtFile
|
||||
|
||||
val classImportableFqName = container.importableFqName
|
||||
val useImport = classImportableFqName != null && file.importDirectives.any {
|
||||
!it.isAllUnder && it.importPath?.fqnPart()?.parent() == classImportableFqName
|
||||
}
|
||||
val addMemberImport = file.importDirectives.any { !it.isAllUnder && it.importPath?.fqnPart()?.parent() == containerFqName }
|
||||
|
||||
var insertQualifier = true
|
||||
if (useImport) {
|
||||
if (addMemberImport) {
|
||||
psiDocumentManager.commitAllDocuments()
|
||||
if (ImportInsertHelper.getInstance(context.project).importDescriptor(file, memberDescriptor) != ImportDescriptorResult.FAIL) {
|
||||
insertQualifier = false
|
||||
}
|
||||
ImportInsertHelper.getInstance(context.project).importDescriptor(file, memberDescriptor)
|
||||
psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.document)
|
||||
}
|
||||
|
||||
if (insertQualifier) {
|
||||
val prefix = qualifierText + "."
|
||||
|
||||
val offset = context.startOffset
|
||||
context.document.insertString(offset, prefix)
|
||||
context.offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset + prefix.length)
|
||||
|
||||
shortenReferences(context, offset, offset + prefix.length)
|
||||
|
||||
}
|
||||
|
||||
psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.document)
|
||||
super.handleInsert(context)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-11
@@ -31,16 +31,12 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
class InsertHandlerProvider(
|
||||
private val callType: CallType<*>?,
|
||||
private val callType: CallType<*>,
|
||||
expectedInfosCalculator: () -> Collection<ExpectedInfo>
|
||||
) {
|
||||
private val expectedInfos by lazy(LazyThreadSafetyMode.NONE) { expectedInfosCalculator() }
|
||||
|
||||
fun insertHandler(descriptor: DeclarationDescriptor): InsertHandler<LookupElement> {
|
||||
if (callType == null) {
|
||||
error("Cannot create InsertHandler when no CallType known")
|
||||
}
|
||||
|
||||
return when (descriptor) {
|
||||
is FunctionDescriptor -> {
|
||||
when (callType) {
|
||||
@@ -48,7 +44,7 @@ class InsertHandlerProvider(
|
||||
val needTypeArguments = needTypeArguments(descriptor)
|
||||
val parameters = descriptor.valueParameters
|
||||
when (parameters.size) {
|
||||
0 -> KotlinFunctionInsertHandler.Normal(needTypeArguments, inputValueArguments = false)
|
||||
0 -> KotlinFunctionInsertHandler.Normal(callType, needTypeArguments, inputValueArguments = false)
|
||||
|
||||
1 -> {
|
||||
if (callType != CallType.SUPER_MEMBERS) { // for super call we don't suggest to generate "super.foo { ... }" (seems to be non-typical use)
|
||||
@@ -57,28 +53,28 @@ class InsertHandlerProvider(
|
||||
if (getValueParametersCountFromFunctionType(parameterType) <= 1) {
|
||||
// otherwise additional item with lambda template is to be added
|
||||
return KotlinFunctionInsertHandler.Normal(
|
||||
needTypeArguments, inputValueArguments = false,
|
||||
callType, needTypeArguments, inputValueArguments = false,
|
||||
lambdaInfo = GenerateLambdaInfo(parameterType, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KotlinFunctionInsertHandler.Normal(needTypeArguments, inputValueArguments = true)
|
||||
KotlinFunctionInsertHandler.Normal(callType, needTypeArguments, inputValueArguments = true)
|
||||
}
|
||||
|
||||
else -> KotlinFunctionInsertHandler.Normal(needTypeArguments, inputValueArguments = true)
|
||||
else -> KotlinFunctionInsertHandler.Normal(callType, needTypeArguments, inputValueArguments = true)
|
||||
}
|
||||
}
|
||||
|
||||
CallType.INFIX -> KotlinFunctionInsertHandler.Infix
|
||||
|
||||
else -> KotlinFunctionInsertHandler.OnlyName
|
||||
else -> KotlinFunctionInsertHandler.OnlyName(callType)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is PropertyDescriptor -> KotlinPropertyInsertHandler
|
||||
is PropertyDescriptor -> KotlinPropertyInsertHandler(callType)
|
||||
|
||||
is ClassifierDescriptor -> KotlinClassifierInsertHandler
|
||||
|
||||
|
||||
+4
-3
@@ -63,7 +63,7 @@ data /* we need copy() */
|
||||
class LookupElementFactory(
|
||||
val basicFactory: BasicLookupElementFactory,
|
||||
private val receiverTypes: Collection<KotlinType>?,
|
||||
private val callType: CallType<*>?,
|
||||
private val callType: CallType<*>,
|
||||
private val inDescriptor: DeclarationDescriptor,
|
||||
private val contextVariablesProvider: ContextVariablesProvider,
|
||||
private val standardLookupElementsPostProcessor: (LookupElement) -> LookupElement = { it }
|
||||
@@ -188,7 +188,7 @@ class LookupElementFactory(
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
KotlinFunctionInsertHandler.Normal(inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo).handleInsert(context, this)
|
||||
KotlinFunctionInsertHandler.Normal(callType, inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo).handleInsert(context, this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,8 @@ class LookupElementFactory(
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
KotlinFunctionInsertHandler.Normal(inputTypeArguments = needTypeArguments, inputValueArguments = false, argumentText = argumentText).handleInsert(context, this)
|
||||
KotlinFunctionInsertHandler.Normal(callType, inputTypeArguments = needTypeArguments, inputValueArguments = false, argumentText = argumentText)
|
||||
.handleInsert(context, this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-10
@@ -20,14 +20,17 @@ import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.completion.isAfterDot
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.renderer.render
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
|
||||
abstract class KotlinCallableInsertHandler : BaseDeclarationInsertHandler() {
|
||||
abstract class KotlinCallableInsertHandler(val callType: CallType<*>) : BaseDeclarationInsertHandler() {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
super.handleInsert(context, item)
|
||||
|
||||
@@ -40,19 +43,21 @@ abstract class KotlinCallableInsertHandler : BaseDeclarationInsertHandler() {
|
||||
val file = context.file
|
||||
val o = item.`object`
|
||||
if (file is KtFile && o is DeclarationLookupObject) {
|
||||
val descriptor = o.descriptor as? CallableDescriptor
|
||||
if (descriptor != null) {
|
||||
// for completion after dot, import insertion may be required only for extensions
|
||||
if (context.isAfterDot() && descriptor.extensionReceiverParameter == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val descriptor = o.descriptor as? CallableDescriptor ?: return
|
||||
if (descriptor.extensionReceiverParameter != null || callType == CallType.CALLABLE_REFERENCE) {
|
||||
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
||||
runWriteAction {
|
||||
ImportInsertHelper.getInstance(context.getProject()).importDescriptor(file, descriptor)
|
||||
ImportInsertHelper.getInstance(context.project).importDescriptor(file, descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (callType == CallType.DEFAULT) {
|
||||
val fqName = descriptor.importableFqName ?: return
|
||||
context.document.replaceString(context.startOffset, context.tailOffset, fqName.render())
|
||||
|
||||
PsiDocumentManager.getInstance(context.project).commitAllDocuments()
|
||||
ShortenReferences.DEFAULT.process(file, context.startOffset, context.tailOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -27,6 +27,7 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
|
||||
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
|
||||
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtTypeArgumentList
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
@@ -34,15 +35,16 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
class GenerateLambdaInfo(val lambdaType: KotlinType, val explicitParameters: Boolean)
|
||||
|
||||
sealed class KotlinFunctionInsertHandler : KotlinCallableInsertHandler() {
|
||||
sealed class KotlinFunctionInsertHandler(callType: CallType<*>) : KotlinCallableInsertHandler(callType) {
|
||||
|
||||
class Normal(
|
||||
callType: CallType<*>,
|
||||
val inputTypeArguments: Boolean,
|
||||
val inputValueArguments: Boolean,
|
||||
val argumentText: String = "",
|
||||
val lambdaInfo: GenerateLambdaInfo? = null,
|
||||
val argumentsOnly: Boolean = false
|
||||
) : KotlinFunctionInsertHandler() {
|
||||
) : KotlinFunctionInsertHandler(callType) {
|
||||
init {
|
||||
if (lambdaInfo != null) {
|
||||
assert(argumentText == "")
|
||||
@@ -51,12 +53,13 @@ sealed class KotlinFunctionInsertHandler : KotlinCallableInsertHandler() {
|
||||
|
||||
//TODO: add 'data' or special annotation when supported
|
||||
fun copy(
|
||||
callType: CallType<*> = this.callType,
|
||||
inputTypeArguments: Boolean = this.inputTypeArguments,
|
||||
inputValueArguments: Boolean = this.inputValueArguments,
|
||||
argumentText: String = this.argumentText,
|
||||
lambdaInfo: GenerateLambdaInfo? = this.lambdaInfo,
|
||||
argumentsOnly: Boolean = this.argumentsOnly
|
||||
) = Normal(inputTypeArguments, inputValueArguments, argumentText, lambdaInfo, argumentsOnly)
|
||||
) = Normal(callType, inputTypeArguments, inputValueArguments, argumentText, lambdaInfo, argumentsOnly)
|
||||
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
|
||||
@@ -193,7 +196,7 @@ sealed class KotlinFunctionInsertHandler : KotlinCallableInsertHandler() {
|
||||
= CodeStyleSettingsManager.getSettings(project).getCustomSettings(KotlinCodeStyleSettings::class.java)!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD
|
||||
}
|
||||
|
||||
object Infix : KotlinFunctionInsertHandler() {
|
||||
object Infix : KotlinFunctionInsertHandler(CallType.INFIX) {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
super.handleInsert(context, item)
|
||||
|
||||
@@ -207,7 +210,7 @@ sealed class KotlinFunctionInsertHandler : KotlinCallableInsertHandler() {
|
||||
}
|
||||
}
|
||||
|
||||
object OnlyName : KotlinFunctionInsertHandler()
|
||||
class OnlyName(callType: CallType<*>) : KotlinFunctionInsertHandler(callType)
|
||||
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
super.handleInsert(context, item)
|
||||
|
||||
+2
-1
@@ -20,8 +20,9 @@ import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.Lookup
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
|
||||
object KotlinPropertyInsertHandler : KotlinCallableInsertHandler() {
|
||||
class KotlinPropertyInsertHandler(callType: CallType<*>) : KotlinCallableInsertHandler(callType) {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
super.handleInsert(context, item)
|
||||
|
||||
|
||||
+3
-3
@@ -153,7 +153,7 @@ class TypeInstantiationItems(
|
||||
var allLookupStrings = setOf(lookupString)
|
||||
var itemText = lookupString
|
||||
var signatureText: String? = null
|
||||
var typeText = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)
|
||||
val typeText = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)
|
||||
|
||||
val insertHandler: InsertHandler<LookupElement>
|
||||
if (isAbstract) {
|
||||
@@ -218,12 +218,12 @@ class TypeInstantiationItems(
|
||||
}
|
||||
|
||||
val baseInsertHandler = when (visibleConstructors.size) {
|
||||
0 -> KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = false, argumentsOnly = true)
|
||||
0 -> KotlinFunctionInsertHandler.Normal(CallType.DEFAULT, inputTypeArguments = false, inputValueArguments = false, argumentsOnly = true)
|
||||
|
||||
1 -> (lookupElementFactory.insertHandlerProvider.insertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler.Normal)
|
||||
.copy(argumentsOnly = true)
|
||||
|
||||
else -> KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = true, argumentsOnly = true)
|
||||
else -> KotlinFunctionInsertHandler.Normal(CallType.DEFAULT, inputTypeArguments = false, inputValueArguments = true, argumentsOnly = true)
|
||||
}
|
||||
|
||||
insertHandler = object : InsertHandler<LookupElement> {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(x: Outer.Nested) {
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
public class Outer {
|
||||
public interface Nested {
|
||||
void foo();
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(x: Outer.Nested) {
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
foo(Outer.Nested { <caret> })
|
||||
}
|
||||
+21
-5
@@ -17,11 +17,11 @@
|
||||
package org.jetbrains.kotlin.idea.completion.test.handlers
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import junit.framework.TestCase
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import org.jetbrains.kotlin.idea.completion.test.COMPLETION_TEST_DATA_BASE_PATH
|
||||
import org.jetbrains.kotlin.idea.completion.test.KotlinCompletionTestCase
|
||||
import java.io.File
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class SmartCompletionMultifileHandlerTest : KotlinCompletionTestCase() {
|
||||
fun testImportExtensionFunction() { doTest() }
|
||||
@@ -30,12 +30,14 @@ class SmartCompletionMultifileHandlerTest : KotlinCompletionTestCase() {
|
||||
|
||||
fun testAnonymousObjectGenericJava() { doTest() }
|
||||
|
||||
fun testNestedSamAdapter() { doTest(lookupString = "Nested") }
|
||||
|
||||
override fun setUp() {
|
||||
setType(CompletionType.SMART)
|
||||
super.setUp()
|
||||
}
|
||||
|
||||
fun doTest() {
|
||||
private fun doTest(lookupString: String? = null, itemText: String? = null) {
|
||||
val fileName = getTestName(false)
|
||||
|
||||
val fileNames = listOf(fileName + "-1.kt", fileName + "-2.kt", fileName + ".java")
|
||||
@@ -44,8 +46,22 @@ class SmartCompletionMultifileHandlerTest : KotlinCompletionTestCase() {
|
||||
|
||||
complete(1)
|
||||
if (myItems != null) {
|
||||
assertTrue(myItems.size == 1, "Multiple items in completion")
|
||||
selectItem(myItems[0])
|
||||
fun isMatching(lookupElement: LookupElement): Boolean {
|
||||
if (lookupString != null && lookupElement.lookupString != lookupString) return false
|
||||
|
||||
val presentation = LookupElementPresentation()
|
||||
lookupElement.renderElement(presentation)
|
||||
if (itemText != null && presentation.itemText != itemText) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
val items = myItems.filter(::isMatching)
|
||||
when (items.size) {
|
||||
0 -> fail("No matching items found")
|
||||
1 -> selectItem(myItems[0])
|
||||
else -> fail("Multiple matching items found")
|
||||
}
|
||||
}
|
||||
|
||||
checkResultByFile(fileName + ".kt.after")
|
||||
|
||||
Reference in New Issue
Block a user