diff --git a/annotations/com/intellij/codeInsight/lookup/annotations.xml b/annotations/com/intellij/codeInsight/lookup/annotations.xml
index b60cd8c9298..9c0946b6ea6 100644
--- a/annotations/com/intellij/codeInsight/lookup/annotations.xml
+++ b/annotations/com/intellij/codeInsight/lookup/annotations.xml
@@ -83,6 +83,9 @@
name='com.intellij.codeInsight.lookup.LookupElementBuilder com.intellij.codeInsight.lookup.LookupElementBuilder withTypeText(java.lang.String, boolean)'>
+ -
+
+
-
diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/ShortenReferences.kt b/idea/src/org/jetbrains/jet/plugin/codeInsight/ShortenReferences.kt
index 7dfd124f59e..a05f44778dc 100644
--- a/idea/src/org/jetbrains/jet/plugin/codeInsight/ShortenReferences.kt
+++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/ShortenReferences.kt
@@ -61,7 +61,7 @@ public object ShortenReferences {
}
private fun process(elements: Iterable, elementFilter: (PsiElement) -> FilterResult) {
- for ((file, fileElements) in elements.groupBy { element -> element.getContainingFile() as JetFile }) {
+ for ((file, fileElements) in elements.groupBy { element -> element.getContainingJetFile() }) {
// first resolve all qualified references - optimization
val referenceToContext = JetFileReferencesResolver.resolve(file, fileElements, visitShortNames = false)
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandler.kt b/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandlers.kt
similarity index 63%
rename from idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandler.kt
rename to idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandlers.kt
index bfa508bf7ca..0c5fe2f0ebb 100644
--- a/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandler.kt
+++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandlers.kt
@@ -2,11 +2,14 @@ package org.jetbrains.jet.plugin.completion.handlers
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElement
+import com.intellij.openapi.editor.*
import com.intellij.openapi.editor.event.*
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
-class WithTailInsertHandler(val tailChar: Char, val spaceAfter: Boolean) : InsertHandler {
+abstract class WithTailInsertHandlerBase : InsertHandler {
+ protected abstract fun insertTail(document: Document, offset: Int): Int
+
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val document = context.getDocument()
val caretModel = context.getEditor().getCaretModel()
@@ -37,21 +40,34 @@ class WithTailInsertHandler(val tailChar: Char, val spaceAfter: Boolean) : Inser
}
val moveCaret = caretModel.getOffset() == maxChangeOffset
+ val endTailOffset = insertTail(document, maxChangeOffset)
+ if (moveCaret) {
+ caretModel.moveToOffset(endTailOffset)
+ }
+ }
+}
+class WithTailCharInsertHandler(val tailChar: Char, val spaceAfter: Boolean) : WithTailInsertHandlerBase() {
+ override fun insertTail(document: Document, offset: Int): Int {
fun isCharAt(offset: Int, c: Char) = offset < document.getTextLength() && document.getText(TextRange(offset, offset + 1))[0] == c
- if (isCharAt(maxChangeOffset, tailChar)) {
- document.deleteString(maxChangeOffset, maxChangeOffset + 1)
+ if (isCharAt(offset, tailChar)) {
+ document.deleteString(offset, offset + 1)
- if (spaceAfter && isCharAt(maxChangeOffset, ' ')) {
- document.deleteString(maxChangeOffset, maxChangeOffset + 1)
+ if (spaceAfter && isCharAt(offset, ' ')) {
+ document.deleteString(offset, offset + 1)
}
}
val textToInsert = if (spaceAfter) tailChar + " " else tailChar.toString()
- document.insertString(maxChangeOffset, textToInsert)
- if (moveCaret) {
- caretModel.moveToOffset(maxChangeOffset + textToInsert.length)
- }
+ document.insertString(offset, textToInsert)
+ return offset + textToInsert.length
+ }
+}
+
+class WithTailStringInsertHandler(val tail: String) : WithTailInsertHandlerBase() {
+ override fun insertTail(document: Document, offset: Int): Int {
+ document.insertString(offset, tail)
+ return offset + tail.length
}
}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/smart/LambdaItems.kt b/idea/src/org/jetbrains/jet/plugin/completion/smart/LambdaItems.kt
index fbbbb546e06..d8fe602681a 100644
--- a/idea/src/org/jetbrains/jet/plugin/completion/smart/LambdaItems.kt
+++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/LambdaItems.kt
@@ -29,7 +29,7 @@ class LambdaItems(val project: Project) {
val offerNoParametersLambda = singleSignatureLength == 0 || singleSignatureLength == 1
if (offerNoParametersLambda) {
val lookupElement = createLookupElement("{...}", "{ ", " }", shortenRefs = false)
- collection.add(addTailToLookupElement(lookupElement, functionExpectedInfos))
+ collection.add(lookupElement.addTail(functionExpectedInfos))
}
if (singleSignatureLength != 0) {
@@ -44,7 +44,7 @@ class LambdaItems(val project: Project) {
val lookupElement = LookupElementBuilder.create(lookupString)
.withInsertHandler(LambdaInsertHandler(functionType, useExplicitTypes))
.suppressAutoInsertion()
- collection.add(addTailToLookupElement(lookupElement, functionExpectedInfos.filter { it.`type` == functionType }))
+ collection.add(lookupElement.addTail(functionExpectedInfos.filter { it.`type` == functionType }))
}
}
}
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt b/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt
index ba0ea1a9bde..dc64ee202e8 100644
--- a/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt
+++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt
@@ -55,13 +55,16 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
for (descriptor in referenceVariants) {
if (itemsToSkip.contains(descriptor)) continue
- val matchedExpectedInfos = expectedInfos.filter { expectedInfo ->
- typesWithAutoCasts(descriptor).any { it.isSubtypeOf(expectedInfo.`type`) }
- }
- if (matchedExpectedInfos.isNotEmpty()) {
- val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor)
- result.add(addTailToLookupElement(lookupElement, matchedExpectedInfos))
+ val types = typesWithAutoCasts(descriptor)
+ val nonNullTypes = types.map { TypeUtils.makeNotNullable(it) }
+ val classifier = { (expectedInfo: ExpectedInfo) ->
+ when {
+ types.any { it.isSubtypeOf(expectedInfo.`type`) } -> ExpectedInfoClassification.MATCHES
+ nonNullTypes.any { it.isSubtypeOf(expectedInfo.`type`) } -> ExpectedInfoClassification.MAKE_NOT_NULLABLE
+ else -> ExpectedInfoClassification.NOT_MATCHES
+ }
}
+ result.addLookupElements(expectedInfos, classifier, { DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor) })
if (receiver == null) {
toFunctionReferenceLookupElement(descriptor, functionExpectedInfos)?.let { result.add(it) }
@@ -129,7 +132,7 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
}
}
- return addTailToLookupElement(lookupElement, matchedExpectedInfos)
+ return lookupElement.addTail(matchedExpectedInfos)
}
if (descriptor is SimpleFunctionDescriptor) {
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/smart/StaticMembers.kt b/idea/src/org/jetbrains/jet/plugin/completion/smart/StaticMembers.kt
index 8931f07dd1f..89109794e3d 100644
--- a/idea/src/org/jetbrains/jet/plugin/completion/smart/StaticMembers.kt
+++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/StaticMembers.kt
@@ -21,6 +21,7 @@ import com.intellij.codeInsight.completion.InsertionContext
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.psi.JetExpression
+import java.util.ArrayList
// adds java static members, enum members and members from class object
class StaticMembers(val bindingContext: BindingContext, val resolveSession: ResolveSessionForBodies) {
@@ -45,15 +46,27 @@ class StaticMembers(val bindingContext: BindingContext, val resolveSession: Reso
fun processMember(descriptor: DeclarationDescriptor) {
if (descriptor is DeclarationDescriptorWithVisibility && !Visibilities.isVisible(descriptor, scope.getContainingDeclaration())) return
- val matchedExpectedInfos = expectedInfos.filter {
- expectedInfo ->
- descriptor is CallableDescriptor && descriptor.getReturnType()?.let { it.isSubtypeOf(expectedInfo.`type`) } ?: false
- || DescriptorUtils.isEnumEntry(descriptor) /* we do not need to check type of enum entry because it's taken from proper enum */
+ val classifier: (ExpectedInfo) -> ExpectedInfoClassification
+ if (descriptor is CallableDescriptor) {
+ val returnType = descriptor.getReturnType()
+ if (returnType == null) return
+ classifier = {
+ expectedInfo ->
+ when {
+ returnType.isSubtypeOf(expectedInfo.`type`) -> ExpectedInfoClassification.MATCHES
+ returnType.isNullable() && TypeUtils.makeNotNullable(returnType).isSubtypeOf(expectedInfo.`type`) -> ExpectedInfoClassification.MAKE_NOT_NULLABLE
+ else -> ExpectedInfoClassification.NOT_MATCHES
+ }
+ }
+ }
+ else if (DescriptorUtils.isEnumEntry(descriptor)) {
+ classifier = { ExpectedInfoClassification.MATCHES } /* we do not need to check type of enum entry because it's taken from proper enum */
+ }
+ else{
+ return
}
- if (matchedExpectedInfos.isEmpty()) return
- val lookupElement = createLookupElement(descriptor, classDescriptor)
- collection.add(addTailToLookupElement(lookupElement, matchedExpectedInfos))
+ collection.addLookupElements(expectedInfos, classifier, { createLookupElement(descriptor, classDescriptor) })
}
if (classDescriptor is JavaClassDescriptor) {
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/smart/ThisItems.kt b/idea/src/org/jetbrains/jet/plugin/completion/smart/ThisItems.kt
index 5659ef587a1..c63b4c200e5 100644
--- a/idea/src/org/jetbrains/jet/plugin/completion/smart/ThisItems.kt
+++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/ThisItems.kt
@@ -17,6 +17,7 @@ import org.jetbrains.jet.lang.psi.JetValueArgumentList
import org.jetbrains.jet.lang.psi.JetCallExpression
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import org.jetbrains.jet.lang.resolve.BindingContext
+import org.jetbrains.jet.lang.types.TypeUtils
class ThisItems(val bindingContext: BindingContext) {
public fun addToCollection(collection: MutableCollection, context: JetExpression, expectedInfos: Collection) {
@@ -27,15 +28,21 @@ class ThisItems(val bindingContext: BindingContext) {
for (i in 0..receivers.size - 1) {
val receiver = receivers[i]
val thisType = receiver.getType()
- val matchedExpectedInfos = expectedInfos.filter { thisType.isSubtypeOf(it.`type`) }
- if (matchedExpectedInfos.notEmpty) {
- //TODO: use this code when KT-4258 fixed
- //val expressionText = if (i == 0) "this" else "this@" + (thisQualifierName(receiver, bindingContext) ?: continue)
- val qualifier = if (i == 0) null else thisQualifierName(receiver) ?: continue
- val expressionText = if (qualifier == null) "this" else "this@" + qualifier
- val lookupElement = LookupElementBuilder.create(expressionText).withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType))
- collection.add(addTailToLookupElement(lookupElement, matchedExpectedInfos))
+ val classifier = { (expectedInfo: ExpectedInfo) ->
+ when {
+ thisType.isSubtypeOf(expectedInfo.`type`) -> ExpectedInfoClassification.MATCHES
+ thisType.isNullable() && TypeUtils.makeNotNullable(thisType).isSubtypeOf(expectedInfo.`type`) -> ExpectedInfoClassification.MAKE_NOT_NULLABLE
+ else -> ExpectedInfoClassification.NOT_MATCHES
+ }
}
+ fun lookupElementFactory(): LookupElement? {
+ //TODO: use this code when KT-4258 fixed
+ //val expressionText = if (i == 0) "this" else "this@" + (thisQualifierName(receiver, bindingContext) ?: return null)
+ val qualifier = if (i == 0) null else (thisQualifierName(receiver) ?: return null)
+ val expressionText = if (qualifier == null) "this" else "this@" + qualifier
+ return LookupElementBuilder.create(expressionText).withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType))
+ }
+ collection.addLookupElements(expectedInfos, classifier, ::lookupElementFactory)
}
}
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/smart/TypeInstantiationItems.kt b/idea/src/org/jetbrains/jet/plugin/completion/smart/TypeInstantiationItems.kt
index 797a136dad4..8d49f6b4c83 100644
--- a/idea/src/org/jetbrains/jet/plugin/completion/smart/TypeInstantiationItems.kt
+++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/TypeInstantiationItems.kt
@@ -101,6 +101,6 @@ class TypeInstantiationItems(val bindingContext: BindingContext, val resolveSess
}
}
- collection.add(addTailToLookupElement(lookupElement, tail))
+ collection.add(lookupElement.addTail(tail))
}
}
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt b/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt
index b9d9bbfa3bb..40d3dabf5c2 100644
--- a/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt
+++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt
@@ -5,17 +5,18 @@ import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.psi.PsiDocumentManager
import org.jetbrains.jet.lang.psi.JetFile
-import com.intellij.psi.util.PsiTreeUtil
-import org.jetbrains.jet.lang.psi.JetElement
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
import java.util.HashSet
import com.intellij.codeInsight.lookup.LookupElementDecorator
-import org.jetbrains.jet.plugin.completion.handlers.WithTailInsertHandler
+import org.jetbrains.jet.plugin.completion.handlers.WithTailCharInsertHandler
import com.intellij.codeInsight.lookup.AutoCompletionPolicy
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
+import com.intellij.codeInsight.lookup.LookupElementPresentation
+import org.jetbrains.jet.plugin.completion.handlers.WithTailStringInsertHandler
+import java.util.ArrayList
class ArtificialElementInsertHandler(
val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler{
@@ -42,29 +43,94 @@ fun mergeTails(tails: Collection): Tail? {
return if (HashSet(tails).size == 1) tails.first() else null
}
-fun addTailToLookupElement(lookupElement: LookupElement, tail: Tail?): LookupElement {
+fun LookupElement.addTail(tail: Tail?): LookupElement {
return when (tail) {
- null -> lookupElement
+ null -> this
- Tail.COMMA -> object: LookupElementDecorator(lookupElement) {
+ Tail.COMMA -> object: LookupElementDecorator(this) {
override fun handleInsert(context: InsertionContext) {
- WithTailInsertHandler(',', true /*TODO: use code style option*/).handleInsert(context, lookupElement)
+ WithTailCharInsertHandler(',', true /*TODO: use code style option*/).handleInsert(context, getDelegate())
}
}
- Tail.PARENTHESIS -> object: LookupElementDecorator(lookupElement) {
+ Tail.PARENTHESIS -> object: LookupElementDecorator(this) {
override fun handleInsert(context: InsertionContext) {
- WithTailInsertHandler(')', false).handleInsert(context, lookupElement)
+ WithTailCharInsertHandler(')', false).handleInsert(context, getDelegate())
}
}
}
}
-fun addTailToLookupElement(lookupElement: LookupElement, matchedExpectedInfos: Collection): LookupElement
- = addTailToLookupElement(lookupElement, mergeTails(matchedExpectedInfos.map { it.tail }))
+fun LookupElement.addTail(matchedExpectedInfos: Collection): LookupElement
+ = addTail(mergeTails(matchedExpectedInfos.map { it.tail }))
fun LookupElement.suppressAutoInsertion() = AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(this)
+enum class ExpectedInfoClassification {
+ MATCHES
+ MAKE_NOT_NULLABLE
+ NOT_MATCHES
+}
+
+fun MutableCollection.addLookupElements(expectedInfos: Collection,
+ infoClassifier: (ExpectedInfo) -> ExpectedInfoClassification,
+ lookupElementFactory: () -> LookupElement?) {
+ val matchedInfos = ArrayList()
+ val matchedInfosNotNullable = ArrayList()
+ for (info in expectedInfos) {
+ when (infoClassifier(info)) {
+ ExpectedInfoClassification.MATCHES -> matchedInfos.add(info)
+ ExpectedInfoClassification.MAKE_NOT_NULLABLE -> matchedInfosNotNullable.add(info)
+ }
+ }
+
+ if (matchedInfos.isNotEmpty()) {
+ val lookupElement = lookupElementFactory()
+ if (lookupElement != null) {
+ add(lookupElement.addTail(matchedInfos))
+ }
+ }
+ else if (matchedInfosNotNullable.isNotEmpty()) {
+ addLookupElementsForNullable(lookupElementFactory, matchedInfosNotNullable)
+ }
+}
+
+fun MutableCollection.addLookupElementsForNullable(factory: () -> LookupElement?, matchedInfos: Collection) {
+ run {
+ var lookupElement = factory()
+ if (lookupElement != null) {
+ lookupElement = object: LookupElementDecorator(lookupElement!!) {
+ override fun renderElement(presentation: LookupElementPresentation) {
+ super.renderElement(presentation)
+ presentation.setItemText("!! " + presentation.getItemText())
+ }
+ override fun handleInsert(context: InsertionContext) {
+ WithTailStringInsertHandler("!!").handleInsert(context, getDelegate())
+ }
+ }
+ lookupElement = lookupElement!!.suppressAutoInsertion()
+ add(lookupElement!!.addTail(matchedInfos))
+ }
+ }
+
+ run {
+ var lookupElement = factory()
+ if (lookupElement != null) {
+ lookupElement = object: LookupElementDecorator(lookupElement!!) {
+ override fun renderElement(presentation: LookupElementPresentation) {
+ super.renderElement(presentation)
+ presentation.setItemText("?: " + presentation.getItemText())
+ }
+ override fun handleInsert(context: InsertionContext) {
+ WithTailStringInsertHandler(" ?: ").handleInsert(context, getDelegate()) //TODO: code style
+ }
+ }
+ lookupElement = lookupElement!!.suppressAutoInsertion()
+ add(lookupElement!!.addTail(matchedInfos))
+ }
+ }
+}
+
fun functionType(function: FunctionDescriptor): JetType? {
return KotlinBuiltIns.getInstance().getKFunctionType(function.getAnnotations(),
null,
diff --git a/idea/testData/completion/handlers/smart/NullableValue1.kt b/idea/testData/completion/handlers/smart/NullableValue1.kt
new file mode 100644
index 00000000000..5aa470b0e73
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/NullableValue1.kt
@@ -0,0 +1,7 @@
+fun foo(s: String, c: Char){ }
+
+fun bar(sss: String?) {
+ foo()
+}
+
+// ELEMENT_TEXT: "!! sss"
diff --git a/idea/testData/completion/handlers/smart/NullableValue1.kt.after b/idea/testData/completion/handlers/smart/NullableValue1.kt.after
new file mode 100644
index 00000000000..7c41567d43e
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/NullableValue1.kt.after
@@ -0,0 +1,7 @@
+fun foo(s: String, c: Char){ }
+
+fun bar(sss: String?) {
+ foo(sss!!, )
+}
+
+// ELEMENT_TEXT: "!! sss"
diff --git a/idea/testData/completion/handlers/smart/NullableValue2.kt b/idea/testData/completion/handlers/smart/NullableValue2.kt
new file mode 100644
index 00000000000..2256819947a
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/NullableValue2.kt
@@ -0,0 +1,9 @@
+fun foo(s: String){ }
+
+fun getString(i: Int): String?{}
+
+fun bar() {
+ foo()
+}
+
+// ELEMENT_TEXT: "?: getString(i: Int)"
diff --git a/idea/testData/completion/handlers/smart/NullableValue2.kt.after b/idea/testData/completion/handlers/smart/NullableValue2.kt.after
new file mode 100644
index 00000000000..0d28b7437ff
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/NullableValue2.kt.after
@@ -0,0 +1,9 @@
+fun foo(s: String){ }
+
+fun getString(i: Int): String?{}
+
+fun bar() {
+ foo(getString() ?: )
+}
+
+// ELEMENT_TEXT: "?: getString(i: Int)"
diff --git a/idea/testData/completion/handlers/smart/NullableValue3.kt b/idea/testData/completion/handlers/smart/NullableValue3.kt
new file mode 100644
index 00000000000..c1604bc16d1
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/NullableValue3.kt
@@ -0,0 +1,11 @@
+class K {
+ class object {
+ fun bar(): K? = K()
+ }
+}
+
+fun foo(){
+ val k : K =
+}
+
+// ELEMENT_TEXT: "!! K.bar()"
diff --git a/idea/testData/completion/handlers/smart/NullableValue3.kt.after b/idea/testData/completion/handlers/smart/NullableValue3.kt.after
new file mode 100644
index 00000000000..274bd34fb0c
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/NullableValue3.kt.after
@@ -0,0 +1,11 @@
+class K {
+ class object {
+ fun bar(): K? = K()
+ }
+}
+
+fun foo(){
+ val k : K = K.bar()!!
+}
+
+// ELEMENT_TEXT: "!! K.bar()"
diff --git a/idea/testData/completion/smart/AutoNotNullTypeWithQualifier.kt b/idea/testData/completion/smart/AutoNotNullTypeWithQualifier.kt
index 18781eed32b..674679b98f7 100644
--- a/idea/testData/completion/smart/AutoNotNullTypeWithQualifier.kt
+++ b/idea/testData/completion/smart/AutoNotNullTypeWithQualifier.kt
@@ -7,4 +7,6 @@ class Foo(val prop1 : String?, val prop2 : String?){
}
// EXIST: prop1
-// ABSENT: prop2
+// ABSENT: { lookupString:"prop2", itemText:"prop2" }
+// EXIST: { lookupString:"prop2", itemText:"!! prop2" }
+// EXIST: { lookupString:"prop2", itemText:"?: prop2" }
diff --git a/idea/testData/completion/smart/ClassObjectMembers.kt b/idea/testData/completion/smart/ClassObjectMembers.kt
index 6c4b0b217f3..a511fe34741 100644
--- a/idea/testData/completion/smart/ClassObjectMembers.kt
+++ b/idea/testData/completion/smart/ClassObjectMembers.kt
@@ -17,5 +17,7 @@ fun foo(){
// EXIST: { lookupString:"K.foo", itemText:"K.foo", tailText:" (sample)", typeText:"K" }
// EXIST: { lookupString:"K.bar", itemText:"K.bar()", tailText:" (sample)", typeText:"K" }
// ABSENT: K.x
-// ABSENT: K.kk
+// ABSENT: { itemText:"K.kk" }
+// EXIST: { lookupString:"K.kk", itemText:"!! K.kk", tailText:" (sample)", typeText:"K?" }
+// EXIST: { lookupString:"K.kk", itemText:"?: K.kk", tailText:" (sample)", typeText:"K?" }
// ABSENT: K.privateVal
diff --git a/idea/testData/completion/smart/EmptyPrefix.kt b/idea/testData/completion/smart/EmptyPrefix.kt
index 6c87b561d70..74e0927cbb7 100644
--- a/idea/testData/completion/smart/EmptyPrefix.kt
+++ b/idea/testData/completion/smart/EmptyPrefix.kt
@@ -20,7 +20,9 @@ fun f3() : String{}
// EXIST: p1
// EXIST: p2
// ABSENT: p3
-// ABSENT: p4
+// ABSENT: { lookupString:"p4", itemText:"p4" }
+// EXIST: { lookupString:"p4", itemText:"!! p4" }
+// EXIST: { lookupString:"p4", itemText:"?: p4" }
// EXIST: f1
// EXIST: f2
// ABSENT: f3
diff --git a/idea/testData/completion/smart/NullableThis.kt b/idea/testData/completion/smart/NullableThis.kt
new file mode 100644
index 00000000000..50696961bf5
--- /dev/null
+++ b/idea/testData/completion/smart/NullableThis.kt
@@ -0,0 +1,6 @@
+fun String?.foo(){
+ val s : String =
+}
+
+// EXIST: { lookupString:"this", itemText: "!! this", typeText:"String?" }
+// EXIST: { lookupString:"this", itemText: "?: this", typeText:"String?" }
diff --git a/idea/tests/org/jetbrains/jet/completion/JvmSmartCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/JvmSmartCompletionTestGenerated.java
index c159ca610fe..4558bacebd7 100644
--- a/idea/tests/org/jetbrains/jet/completion/JvmSmartCompletionTestGenerated.java
+++ b/idea/tests/org/jetbrains/jet/completion/JvmSmartCompletionTestGenerated.java
@@ -256,6 +256,11 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest("idea/testData/completion/smart/NotSillyAssignment.kt");
}
+ @TestMetadata("NullableThis.kt")
+ public void testNullableThis() throws Exception {
+ doTest("idea/testData/completion/smart/NullableThis.kt");
+ }
+
@TestMetadata("OverloadedConstructorArgument.kt")
public void testOverloadedConstructorArgument() throws Exception {
doTest("idea/testData/completion/smart/OverloadedConstructorArgument.kt");
diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/AbstractSmartCompletionHandlerTest.kt b/idea/tests/org/jetbrains/jet/completion/handlers/AbstractSmartCompletionHandlerTest.kt
index 85e7e3ca8bf..ad25a4952d0 100644
--- a/idea/tests/org/jetbrains/jet/completion/handlers/AbstractSmartCompletionHandlerTest.kt
+++ b/idea/tests/org/jetbrains/jet/completion/handlers/AbstractSmartCompletionHandlerTest.kt
@@ -37,6 +37,7 @@ import org.jetbrains.jet.InTextDirectivesUtils
abstract class AbstractSmartCompletionHandlerTest() : CompletionHandlerTestBase() {
private val INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:"
private val LOOKUP_STRING_PREFIX = "ELEMENT:"
+ private val ELEMENT_TEXT_PREFIX = "ELEMENT_TEXT:"
private val TAIL_TEXT_PREFIX = "TAIL_TEXT:"
private val COMPLETION_CHAR_PREFIX = "CHAR:"
@@ -49,6 +50,7 @@ abstract class AbstractSmartCompletionHandlerTest() : CompletionHandlerTestBase(
val fileText = fixture.getFile()!!.getText()
val invocationCount = InTextDirectivesUtils.getPrefixedInt(fileText, INVOCATION_COUNT_PREFIX) ?: 1
val lookupString = InTextDirectivesUtils.findStringWithPrefixes(fileText, LOOKUP_STRING_PREFIX)
+ val itemText = InTextDirectivesUtils.findStringWithPrefixes(fileText, ELEMENT_TEXT_PREFIX)
val tailText = InTextDirectivesUtils.findStringWithPrefixes(fileText, TAIL_TEXT_PREFIX)
val completionCharString = InTextDirectivesUtils.findStringWithPrefixes(fileText, COMPLETION_CHAR_PREFIX)
val completionChar = when(completionCharString) {
@@ -56,6 +58,6 @@ abstract class AbstractSmartCompletionHandlerTest() : CompletionHandlerTestBase(
"\\t" -> '\t'
else -> error("Uknown completion char")
}
- doTestWithTextLoaded(invocationCount, lookupString, tailText, completionChar)
+ doTestWithTextLoaded(invocationCount, lookupString, itemText, tailText, completionChar)
}
}
diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionHandlerTestBase.kt b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionHandlerTestBase.kt
index 719e8fccda6..d56e74dc2f9 100644
--- a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionHandlerTestBase.kt
+++ b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionHandlerTestBase.kt
@@ -42,20 +42,24 @@ public abstract class CompletionHandlerTestBase() : JetLightCodeInsightFixtureTe
protected override fun setUp() {
super.setUp()
- fixture = myFixture!!
+ fixture = myFixture
}
- protected fun doTest() : Unit = doTest(2, null, null, '\n')
+ protected fun doTest() : Unit = doTest(2, null, null, null, '\n')
protected fun doTest(time : Int, lookupString : String?, tailText : String?, completionChar : Char) {
- fixture.configureByFile(fileName())
- doTestWithTextLoaded(time, lookupString, tailText, completionChar)
+ doTest(time, lookupString, null, tailText, completionChar)
}
- protected fun doTestWithTextLoaded(time : Int, lookupString : String?, tailText : String?, completionChar : Char) {
- if (lookupString != null || tailText != null) {
+ protected fun doTest(time : Int, lookupString : String?, itemText: String?, tailText : String?, completionChar : Char) {
+ fixture.configureByFile(fileName())
+ doTestWithTextLoaded(time, lookupString, itemText, tailText, completionChar)
+ }
+
+ protected fun doTestWithTextLoaded(time : Int, lookupString : String?, itemText: String?, tailText : String?, completionChar : Char) {
+ if (lookupString != null || itemText != null || tailText != null) {
fixture.complete(completionType, time)
- val item = getExistentLookupElement(lookupString, tailText)
+ val item = getExistentLookupElement(lookupString, itemText, tailText)
if (item != null) {
selectItem(item, completionChar)
}
@@ -70,38 +74,42 @@ public abstract class CompletionHandlerTestBase() : JetLightCodeInsightFixtureTe
fixture.checkResultByFile(afterFileName())
}
- private fun getExistentLookupElement(lookupString : String?, tailText : String?) : LookupElement? {
+ private fun getExistentLookupElement(lookupString : String?, itemText: String?, tailText : String?) : LookupElement? {
val lookup = LookupManager.getInstance(getProject())?.getActiveLookup() as LookupImpl?
if (lookup == null) return null
var foundElement : LookupElement? = null
val presentation = LookupElementPresentation()
for (lookupElement in lookup.getItems()) {
- val lookupOk : Boolean
- if (lookupString != null) {
- lookupOk = lookupElement.getLookupString().contains(lookupString)
- }
- else {
- lookupOk = true
- }
+ val lookupOk = if (lookupString != null) lookupElement.getLookupString().contains(lookupString) else true
if (lookupOk) {
- val tailOk : Boolean
- if (tailText != null) {
- lookupElement.renderElement(presentation)
- val itemTailText : String? = presentation.getTailText()
- tailOk = itemTailText != null && itemTailText.contains(tailText)
+ lookupElement.renderElement(presentation)
+
+ val textOk = if (itemText != null) {
+ val itemItemText = presentation.getItemText()
+ itemItemText != null && itemItemText.contains(itemText)
}
else {
- tailOk = true
+ true
}
- if (tailOk) {
- if (foundElement != null) {
- Assert.fail("Several elements satisfy to completion restrictions")
+ if (textOk) {
+ val tailOk = if (tailText != null) {
+ val itemTailText = presentation.getTailText()
+ itemTailText != null && itemTailText.contains(tailText)
+ }
+ else {
+ true
}
- foundElement = lookupElement
+ if (tailOk) {
+ if (foundElement != null) {
+ Assert.fail("Several elements satisfy to completion restrictions")
+ }
+
+ foundElement = lookupElement
+ }
}
}
}
diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java
index 7a2b55e0e46..70e855c00f5 100644
--- a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java
+++ b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java
@@ -246,6 +246,21 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest("idea/testData/completion/handlers/smart/MergeTail4.kt");
}
+ @TestMetadata("NullableValue1.kt")
+ public void testNullableValue1() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/NullableValue1.kt");
+ }
+
+ @TestMetadata("NullableValue2.kt")
+ public void testNullableValue2() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/NullableValue2.kt");
+ }
+
+ @TestMetadata("NullableValue3.kt")
+ public void testNullableValue3() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/NullableValue3.kt");
+ }
+
@TestMetadata("TabReplaceComma1.kt")
public void testTabReplaceComma1() throws Exception {
doTest("idea/testData/completion/handlers/smart/TabReplaceComma1.kt");