Fixed bug in completion with double insertion of "::class.java"

This commit is contained in:
Valentin Kipyatkov
2015-10-17 02:51:56 +03:00
parent 499ca02615
commit d0f318d7db
9 changed files with 61 additions and 37 deletions
@@ -89,7 +89,7 @@ fun LookupElement.withReceiverCast(): LookupElement {
return object: LookupElementDecorator<LookupElement>(this) { return object: LookupElementDecorator<LookupElement>(this) {
override fun handleInsert(context: InsertionContext) { override fun handleInsert(context: InsertionContext) {
super.handleInsert(context) super.handleInsert(context)
CastReceiverInsertHandler.handleInsert(context, getDelegate()) CastReceiverInsertHandler.postHandleInsert(context, delegate)
} }
} }
} }
@@ -19,22 +19,14 @@ package org.jetbrains.kotlin.idea.completion.handlers
import com.intellij.codeInsight.completion.InsertHandler import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.renderer.render
open class BaseDeclarationInsertHandler : InsertHandler<LookupElement> { open class BaseDeclarationInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) { override fun handleInsert(context: InsertionContext, item: LookupElement) {
val name = (item.getObject() as? DeclarationLookupObject)?.name val name = (item.`object` as? DeclarationLookupObject)?.name
if (name != null) { if (name != null && !name.isSpecial) {
val nameInCode = name.render() context.document.replaceString(context.startOffset, context.tailOffset, name.render())
val document = context.getDocument()
val needEscaping = nameInCode != name.asString()
// we check that text inserted matches the name because something else can be inserted by custom insert handler
if (needEscaping && document.getText(TextRange(context.getStartOffset(), context.getTailOffset())) == name.asString()) {
document.replaceString(context.getStartOffset(), context.getTailOffset(), nameInCode)
}
} }
} }
} }
@@ -26,10 +26,8 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
object CastReceiverInsertHandler : KotlinCallableInsertHandler() { object CastReceiverInsertHandler {
override fun handleInsert(context: InsertionContext, item: LookupElement) { fun postHandleInsert(context: InsertionContext, item: LookupElement) {
super.handleInsert(context, item)
val expression = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), javaClass<JetSimpleNameExpression>(), false) val expression = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), javaClass<JetSimpleNameExpression>(), false)
val qualifiedExpression = PsiTreeUtil.getParentOfType(expression, javaClass<JetQualifiedExpression>(), true) val qualifiedExpression = PsiTreeUtil.getParentOfType(expression, javaClass<JetQualifiedExpression>(), true)
if (qualifiedExpression != null) { if (qualifiedExpression != null) {
@@ -33,15 +33,14 @@ import org.jetbrains.kotlin.types.JetType
class GenerateLambdaInfo(val lambdaType: JetType, val explicitParameters: Boolean) class GenerateLambdaInfo(val lambdaType: JetType, val explicitParameters: Boolean)
sealed class KotlinFunctionInsertHandler( sealed class KotlinFunctionInsertHandler : KotlinCallableInsertHandler() {
) : KotlinCallableInsertHandler() {
class Normal( class Normal(
val inputTypeArguments: Boolean, val inputTypeArguments: Boolean,
val inputValueArguments: Boolean, val inputValueArguments: Boolean,
val argumentText: String = "", val argumentText: String = "",
val lambdaInfo: GenerateLambdaInfo? = null val lambdaInfo: GenerateLambdaInfo? = null,
val argumentsOnly: Boolean = false
) : KotlinFunctionInsertHandler() { ) : KotlinFunctionInsertHandler() {
init { init {
if (lambdaInfo != null) { if (lambdaInfo != null) {
@@ -49,8 +48,19 @@ sealed class KotlinFunctionInsertHandler(
} }
} }
public override fun handleInsert(context: InsertionContext, item: LookupElement) { //TODO: add 'data' or special annotation when supported
super.handleInsert(context, item) fun copy(
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)
override fun handleInsert(context: InsertionContext, item: LookupElement) {
if (!argumentsOnly) {
super.handleInsert(context, item)
}
val psiDocumentManager = PsiDocumentManager.getInstance(context.project) val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
psiDocumentManager.commitAllDocuments() psiDocumentManager.commitAllDocuments()
@@ -16,10 +16,12 @@
package org.jetbrains.kotlin.idea.completion.smart package org.jetbrains.kotlin.idea.completion.smart
import com.intellij.codeInsight.completion.CompletionInitializationContext
import com.intellij.codeInsight.completion.InsertionContext import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.psi.PsiDocumentManager
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.completion.ExpectedInfo import org.jetbrains.kotlin.idea.completion.ExpectedInfo
import org.jetbrains.kotlin.idea.completion.LookupElementFactory import org.jetbrains.kotlin.idea.completion.LookupElementFactory
@@ -30,7 +32,6 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.fuzzyReturnType import org.jetbrains.kotlin.idea.util.fuzzyReturnType
import org.jetbrains.kotlin.psi.JetSimpleNameExpression import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
@@ -109,9 +110,7 @@ class StaticMembers(
return lookupElements.map { return lookupElements.map {
object: LookupElementDecorator<LookupElement>(it) { object: LookupElementDecorator<LookupElement>(it) {
override fun getAllLookupStrings(): Set<String> { override fun getAllLookupStrings() = setOf(delegate.lookupString, qualifierPresentation)
return setOf(it.lookupString, qualifierPresentation)
}
override fun renderElement(presentation: LookupElementPresentation) { override fun renderElement(presentation: LookupElementPresentation) {
getDelegate().renderElement(presentation) getDelegate().renderElement(presentation)
@@ -132,16 +131,16 @@ class StaticMembers(
} }
override fun handleInsert(context: InsertionContext) { override fun handleInsert(context: InsertionContext) {
var text = qualifierText + "." + memberDescriptor.getName().render() val prefix = qualifierText + "."
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), text) val offset = context.startOffset
context.setTailOffset(context.getStartOffset() + text.length()) context.document.insertString(offset, prefix)
context.offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset + prefix.length)
if (memberDescriptor is FunctionDescriptor) { shortenReferences(context, offset, offset + prefix.length)
getDelegate().handleInsert(context) PsiDocumentManager.getInstance(context.project).doPostponedOperationsAndUnblockDocument(context.document)
}
shortenReferences(context, context.getStartOffset(), context.getTailOffset()) super.handleInsert(context)
} }
}.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER) }.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER)
} }
@@ -202,10 +202,13 @@ class TypeInstantiationItems(
else -> "(...)" else -> "(...)"
} }
val baseInsertHandler = when (visibleConstructors.size()) { val baseInsertHandler = when (visibleConstructors.size) {
0 -> KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = false) 0 -> KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = false, argumentsOnly = true)
1 -> lookupElementFactory.insertHandlerProvider.insertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler.Normal
else -> KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = true) 1 -> (lookupElementFactory.insertHandlerProvider.insertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler.Normal)
.copy(argumentsOnly = true)
else -> KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = true, argumentsOnly = true)
} }
insertHandler = object : InsertHandler<LookupElement> { insertHandler = object : InsertHandler<LookupElement> {
@@ -0,0 +1,8 @@
fun <T> foo(klass: Class<T>) {}
fun bar() {
foo<String>(<caret>)
}
// ELEMENT: "String::class.java"
@@ -0,0 +1,8 @@
fun <T> foo(klass: Class<T>) {}
fun bar() {
foo<String>(String::class.java)<caret>
}
// ELEMENT: "String::class.java"
@@ -269,6 +269,12 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest(fileName); doTest(fileName);
} }
@TestMetadata("ConcreteJavaClass2.kt")
public void testConcreteJavaClass2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/ConcreteJavaClass2.kt");
doTest(fileName);
}
@TestMetadata("ConcreteKClass.kt") @TestMetadata("ConcreteKClass.kt")
public void testConcreteKClass() throws Exception { public void testConcreteKClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/ConcreteKClass.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/ConcreteKClass.kt");