diff --git a/annotations/com/intellij/openapi/editor/event/annotations.xml b/annotations/com/intellij/openapi/editor/event/annotations.xml
new file mode 100644
index 00000000000..a60f0e99d37
--- /dev/null
+++ b/annotations/com/intellij/openapi/editor/event/annotations.xml
@@ -0,0 +1,9 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.java b/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.java
index 8d32ed66235..6298b3badc1 100644
--- a/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.java
+++ b/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.java
@@ -16,8 +16,6 @@
package org.jetbrains.jet.plugin.completion;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
@@ -29,6 +27,7 @@ import com.intellij.openapi.util.Conditions;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
+import kotlin.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
@@ -52,6 +51,13 @@ class CompletionSession {
private final JetCompletionResultSet jetResult;
private final JetSimpleNameReference jetReference;
+ private final Condition descriptorFilter = new Condition() {
+ @Override
+ public boolean value(DeclarationDescriptor descriptor) {
+ return isVisibleDescriptor(descriptor);
+ }
+ };
+
public CompletionSession(
@NotNull CompletionParameters parameters,
@NotNull CompletionResultSet result,
@@ -71,12 +77,7 @@ class CompletionSession {
this.jetResult = new JetCompletionResultSet(
WeigherPackage.addJetSorting(result, parameters),
resolveSession,
- expressionBindingContext, new Condition() {
- @Override
- public boolean value(DeclarationDescriptor descriptor) {
- return isVisibleDescriptor(descriptor);
- }
- });
+ expressionBindingContext, descriptorFilter);
}
public void completeForReference() {
@@ -134,10 +135,10 @@ class CompletionSession {
final SmartCompletionData data = CompletionPackage.buildSmartCompletionData(jetReference.getExpression(), getResolveSession());
if (data != null) {
- addReferenceVariants(new Condition() {
+ addReferenceVariants(new Function1() {
@Override
- public boolean value(DeclarationDescriptor descriptor) {
- return data.accepts(descriptor);
+ public LookupElement invoke(DeclarationDescriptor descriptor) {
+ return data.toElement(descriptor);
}
});
for (LookupElement element : data.getAdditionalElements()) {
@@ -241,17 +242,26 @@ class CompletionSession {
}
private void addReferenceVariants(@NotNull final Condition filterCondition) {
+ addReferenceVariants(new Function1() {
+ @Override
+ public LookupElement invoke(DeclarationDescriptor descriptor) {
+ return filterCondition.value(descriptor) ? DescriptorLookupConverter.createLookupElement(getResolveSession(), getExpressionBindingContext(), descriptor) : null;
+ }
+ });
+ }
+
+ private void addReferenceVariants(@NotNull Function1 filter) {
Collection descriptors = TipsManager.getReferenceVariants(
jetReference.getExpression(), getExpressionBindingContext());
- Collection filterDescriptors = Collections2.filter(descriptors, new Predicate() {
- @Override
- public boolean apply(@Nullable DeclarationDescriptor descriptor) {
- return descriptor != null && filterCondition.value(descriptor);
+ for (DeclarationDescriptor descriptor : descriptors) {
+ if (descriptor != null && descriptorFilter.value(descriptor)) {
+ LookupElement element = filter.invoke(descriptor);
+ if (element != null) {
+ jetResult.addElement(element);
+ }
}
- });
-
- jetResult.addAllElements(filterDescriptors);
+ }
}
private boolean isVisibleDescriptor(DeclarationDescriptor descriptor) {
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt b/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt
index 3cdcd03babf..ffbfb7d3998 100644
--- a/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt
+++ b/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt
@@ -15,27 +15,14 @@ import com.google.common.collect.SetMultimap
import java.util.*
import org.jetbrains.jet.lang.resolve.calls.autocasts.*
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
-import org.jetbrains.jet.lang.resolve.scopes.JetScope
-import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration
-import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration
import org.jetbrains.jet.lang.resolve.name.Name
-import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPropertyDescriptor
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaClassDescriptor
-import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiClass
-import com.intellij.psi.PsiModifier
import com.intellij.psi.util.PsiTreeUtil
-import com.intellij.openapi.editor.EditorModificationUtil
import org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler
import com.intellij.psi.PsiDocumentManager
-import com.intellij.openapi.application.ApplicationManager
-import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
-import org.jetbrains.jet.utils.*
import org.jetbrains.jet.di.InjectorForMacros
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext
-import org.jetbrains.jet.lang.resolve.calls.context.SimpleResolutionContext
import org.jetbrains.jet.lang.resolve.calls.context.ContextDependency
import org.jetbrains.jet.lang.resolve.calls.context.CheckValueArgumentsMode
import org.jetbrains.jet.lang.resolve.calls.CompositeExtension
@@ -47,10 +34,16 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
import com.intellij.lang.ASTNode
trait SmartCompletionData{
- fun accepts(descriptor: DeclarationDescriptor): Boolean
+ fun toElement(descriptor: DeclarationDescriptor): LookupElement?
val additionalElements: Iterable
}
+enum class Tail {
+ COMMA
+}
+
+data class ExpectedTypeInfo(val `type`: JetType, val tail: Tail?)
+
fun buildSmartCompletionData(expression: JetSimpleNameExpression, resolveSession: ResolveSessionForBodies): SmartCompletionData? {
val parent = expression.getParent()
val expressionWithType: JetExpression;
@@ -67,7 +60,7 @@ fun buildSmartCompletionData(expression: JetSimpleNameExpression, resolveSession
val bindingContext = resolveSession.resolveToElement(expressionWithType)
val allExpectedTypes = calcExpectedTypes(expressionWithType, bindingContext, resolveSession.getModuleDescriptor()) ?: return null
- val expectedTypes = allExpectedTypes.filter { !it.isError() }
+ val expectedTypes = allExpectedTypes.filter { !it.`type`.isError() }
if (expectedTypes.isEmpty()) return null
val itemsToSkip = calcItemsToSkip(expressionWithType, resolveSession)
@@ -86,7 +79,7 @@ fun buildSmartCompletionData(expression: JetSimpleNameExpression, resolveSession
thisItems(expressionWithType, expectedTypes, bindingContext).toCollection(additionalElements)
}
- val dataFlowInfo = bindingContext.get(BindingContext.EXPRESSION_DATA_FLOW_INFO, expressionWithType)
+ val dataFlowInfo = bindingContext[BindingContext.EXPRESSION_DATA_FLOW_INFO, expressionWithType]
val (variableToTypes: Map>, notNullVariables: Set) = processDataFlowInfo(dataFlowInfo, receiver, bindingContext)
fun typesOf(descriptor: DeclarationDescriptor): Iterable {
@@ -116,28 +109,30 @@ fun buildSmartCompletionData(expression: JetSimpleNameExpression, resolveSession
}
return object: SmartCompletionData {
- override fun accepts(descriptor: DeclarationDescriptor)
- = !itemsToSkip.contains(descriptor)
- && typesOf(descriptor).any {
- descriptorType -> expectedTypes.any { expectedType -> isSubtypeOf(descriptorType, expectedType) }
- }
+ override fun toElement(descriptor: DeclarationDescriptor): LookupElement? {
+ if (itemsToSkip.contains(descriptor)) return null
+ val matchedExpectedTypes = expectedTypes.filter { expectedType -> typesOf(descriptor).any { descriptorType -> isSubtypeOf(descriptorType, expectedType.`type`) } }
+ if (matchedExpectedTypes.isEmpty()) return null
+ val tail = mergeTails(matchedExpectedTypes.map { it.tail })
+ return decorateLookupElement(DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor), tail)
+ }
override val additionalElements = additionalElements
}
}
-private fun calcExpectedTypes(expressionWithType: JetExpression, bindingContext: BindingContext, moduleDescriptor: ModuleDescriptor): Collection? {
+private fun calcExpectedTypes(expressionWithType: JetExpression, bindingContext: BindingContext, moduleDescriptor: ModuleDescriptor): Collection? {
val expectedTypes = calcArgumentExpectedTypes(expressionWithType, bindingContext, moduleDescriptor)
if (expectedTypes != null) {
return expectedTypes
}
else{
- val expectedType = bindingContext.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType) ?: return null
- return listOf(expectedType)
+ val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
+ return listOf(ExpectedTypeInfo(expectedType, null))
}
}
-private fun calcArgumentExpectedTypes(expressionWithType: JetExpression, bindingContext: BindingContext, moduleDescriptor: ModuleDescriptor): Collection? {
+private fun calcArgumentExpectedTypes(expressionWithType: JetExpression, bindingContext: BindingContext, moduleDescriptor: ModuleDescriptor): Collection? {
val argument = expressionWithType.getParent() as? JetValueArgument ?: return null
if (argument.isNamed()) return null //TODO - support named arguments (also do not forget to check for presence of named arguments before)
val argumentList = argument.getParent() as JetValueArgumentList
@@ -150,7 +145,7 @@ private fun calcArgumentExpectedTypes(expressionWithType: JetExpression, binding
val callOperationNode: ASTNode?
if (parent is JetQualifiedExpression && callExpression == parent.getSelectorExpression()) {
val receiverExpression = parent.getReceiverExpression()
- val expressionType = bindingContext.get(BindingContext.EXPRESSION_TYPE, receiverExpression) ?: return null
+ val expressionType = bindingContext[BindingContext.EXPRESSION_TYPE, receiverExpression] ?: return null
receiver = ExpressionReceiver(receiverExpression, expressionType)
callOperationNode = parent.getOperationTokenNode()
}
@@ -160,14 +155,14 @@ private fun calcArgumentExpectedTypes(expressionWithType: JetExpression, binding
}
val call = CallMaker.makeCall(receiver, callOperationNode, callExpression)
- val resolutionScope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, calleeExpression) ?: return null //TODO: discuss it
+ val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, calleeExpression] ?: return null //TODO: discuss it
val callResolutionContext = BasicCallResolutionContext.create(
DelegatingBindingTrace(bindingContext, "Temporary trace for smart completion"),
resolutionScope,
call,
- bindingContext.get(BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression) ?: TypeUtils.NO_EXPECTED_TYPE,
- bindingContext.get(BindingContext.EXPRESSION_DATA_FLOW_INFO, callExpression) ?: DataFlowInfo.EMPTY,
+ bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression] ?: TypeUtils.NO_EXPECTED_TYPE,
+ bindingContext[BindingContext.EXPRESSION_DATA_FLOW_INFO, callExpression] ?: DataFlowInfo.EMPTY,
ContextDependency.INDEPENDENT,
CheckValueArgumentsMode.ENABLED,
CompositeExtension(listOf()),
@@ -175,12 +170,13 @@ private fun calcArgumentExpectedTypes(expressionWithType: JetExpression, binding
val callResolver = InjectorForMacros(expressionWithType.getProject(), moduleDescriptor).getCallResolver()!!
val results: OverloadResolutionResults = callResolver.resolveFunctionCall(callResolutionContext)
- val expectedTypes = HashSet()
+ val expectedTypes = HashSet()
for (candidate: ResolvedCall in results.getAllCandidates()!!) {
val parameters = candidate.getResultingDescriptor().getValueParameters()
if (parameters.size <= argumentIndex) continue
val parameterDescriptor = parameters[argumentIndex]
- expectedTypes.add(parameterDescriptor.getType())
+ val tail = if (argumentIndex == parameters.size - 1) null else Tail.COMMA
+ expectedTypes.add(ExpectedTypeInfo(parameterDescriptor.getType(), tail))
}
return expectedTypes
}
@@ -191,7 +187,7 @@ private fun calcItemsToSkip(expression: JetExpression, resolveSession: ResolveSe
is JetProperty -> {
//TODO: this can be filtered out by ordinary completion
if (expression == parent.getInitializer()) {
- return resolveSession.resolveToElement(parent).get(BindingContext.DECLARATION_TO_DESCRIPTOR, parent).toList()
+ return resolveSession.resolveToElement(parent)[BindingContext.DECLARATION_TO_DESCRIPTOR, parent].toList()
}
}
@@ -199,7 +195,7 @@ private fun calcItemsToSkip(expression: JetExpression, resolveSession: ResolveSe
if (parent.getRight() == expression && parent.getOperationToken() == JetTokens.EQ) {
val left = parent.getLeft()
if (left is JetReferenceExpression) {
- return resolveSession.resolveToElement(left).get(BindingContext.REFERENCE_TARGET, left).toList()
+ return resolveSession.resolveToElement(left)[BindingContext.REFERENCE_TARGET, left].toList()
}
}
}
@@ -207,8 +203,8 @@ private fun calcItemsToSkip(expression: JetExpression, resolveSession: ResolveSe
return listOf()
}
-private fun typeInstantiationItems(expectedType: JetType, resolveSession: ResolveSessionForBodies, bindingContext: BindingContext): Iterable {
- val typeConstructor: TypeConstructor = expectedType.getConstructor()
+private fun typeInstantiationItems(expectedType: ExpectedTypeInfo, resolveSession: ResolveSessionForBodies, bindingContext: BindingContext): Iterable {
+ val typeConstructor: TypeConstructor = expectedType.`type`.getConstructor()
val classifier: ClassifierDescriptor? = typeConstructor.getDeclarationDescriptor()
if (!(classifier is ClassDescriptor)) return listOf()
@@ -218,7 +214,7 @@ private fun typeInstantiationItems(expectedType: JetType, resolveSession: Resolv
var lookupString = lookupElement.getLookupString()
- val typeArgs = expectedType.getArguments()
+ val typeArgs = expectedType.`type`.getArguments()
var itemText = lookupString + DescriptorRenderer.TEXT.renderTypeArguments(typeArgs)
val insertHandler: InsertHandler
@@ -289,16 +285,18 @@ private fun typeInstantiationItems(expectedType: JetType, resolveSession: Resolv
}
}
+ val lookupElementWithTail = decorateLookupElement(lookupElementDecorated, expectedType.tail)
+
if (suppressAutoInsertion) {
- return listOf(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(lookupElementDecorated))
+ return listOf(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(lookupElementWithTail))
}
else{
- return listOf(lookupElementDecorated)
+ return listOf(lookupElementWithTail)
}
}
-private fun thisItems(context: JetExpression, expectedTypes: Collection, bindingContext: BindingContext): Iterable {
- val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, context)
+private fun thisItems(context: JetExpression, expectedTypes: Collection, bindingContext: BindingContext): Iterable {
+ val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
if (scope == null) return listOf()
val receivers: List = scope.getImplicitReceiversHierarchy()
@@ -306,12 +304,15 @@ private fun thisItems(context: JetExpression, expectedTypes: Collection
for (i in 0..receivers.size - 1) {
val receiver = receivers[i]
val thisType = receiver.getType()
- if (expectedTypes.any { isSubtypeOf(thisType, it) }) {
+ val matchedExpectedTypes = expectedTypes.filter { isSubtypeOf(thisType, it.`type`) }
+ if (matchedExpectedTypes.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, bindingContext) ?: continue
val expressionText = if (qualifier == null) "this" else "this@" + qualifier
- result.add(LookupElementBuilder.create(expressionText).withTypeText(DescriptorRenderer.TEXT.renderType(thisType)))
+ val lookupElement = LookupElementBuilder.create(expressionText).withTypeText(DescriptorRenderer.TEXT.renderType(thisType))
+ val tailType = mergeTails(matchedExpectedTypes.map { it.tail })
+ result.add(decorateLookupElement(lookupElement, tailType))
}
}
return result
@@ -344,7 +345,7 @@ private fun processDataFlowInfo(dataFlowInfo: DataFlowInfo?, receiver: JetExpres
if (dataFlowInfo != null) {
val dataFlowValueToVariable: (DataFlowValue) -> VariableDescriptor?
if (receiver != null) {
- val receiverType = bindingContext.get(BindingContext.EXPRESSION_TYPE, receiver)
+ val receiverType = bindingContext[BindingContext.EXPRESSION_TYPE, receiver]
if (receiverType != null) {
val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext).getId()
dataFlowValueToVariable = {(value) ->
@@ -371,7 +372,7 @@ private fun processDataFlowInfo(dataFlowInfo: DataFlowInfo?, receiver: JetExpres
val nullabilityInfo: Map = dataFlowInfo.getCompleteNullabilityInfo()
val notNullVariables = nullabilityInfo
- .filter_tmp { it.getValue() == Nullability.NOT_NULL }
+ .filter { it.getValue() == Nullability.NOT_NULL }
.map { dataFlowValueToVariable(it.getKey()) }
.filterNotNullTo(HashSet())
@@ -382,17 +383,17 @@ private fun processDataFlowInfo(dataFlowInfo: DataFlowInfo?, receiver: JetExpres
}
// adds java static members, enum members and members from class object
-private fun staticMembers(context: JetExpression, expectedType: JetType, resolveSession: ResolveSessionForBodies, bindingContext: BindingContext): Iterable {
- val classDescriptor = TypeUtils.getClassDescriptor(expectedType)
+private fun staticMembers(context: JetExpression, expectedType: ExpectedTypeInfo, resolveSession: ResolveSessionForBodies, bindingContext: BindingContext): Iterable {
+ val classDescriptor = TypeUtils.getClassDescriptor(expectedType.`type`)
if (classDescriptor == null) return listOf()
if (classDescriptor.getName().isSpecial()) return listOf()
- val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, context)
+ val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
if (scope == null) return listOf()
val descriptors = ArrayList()
val isSuitableCallable: (DeclarationDescriptor) -> Boolean = {
- it is CallableDescriptor && it.getReturnType()?.let { isSubtypeOf(it, expectedType) } ?: false
+ it is CallableDescriptor && it.getReturnType()?.let { isSubtypeOf(it, expectedType.`type`) } ?: false
}
if (classDescriptor is JavaClassDescriptor) {
@@ -447,7 +448,7 @@ private fun staticMembers(context: JetExpression, expectedType: JetType, resolve
}
}
- return object: LookupElementDecorator(lookupElement){
+ val lookupElementDecorated = object: LookupElementDecorator(lookupElement) {
override fun getLookupString() = lookupString
override fun renderElement(presentation: LookupElementPresentation) {
@@ -459,7 +460,7 @@ private fun staticMembers(context: JetExpression, expectedType: JetType, resolve
if (descriptor is FunctionDescriptor) {
presentation.appendTailText(tailText, true)
}
- else{
+ else {
presentation.setTailText(tailText, true)
}
@@ -472,11 +473,32 @@ private fun staticMembers(context: JetExpression, expectedType: JetType, resolve
insertHandler.handleInsert(context, lookupElement)
}
}
+
+ return decorateLookupElement(lookupElementDecorated, expectedType.tail)
}
return descriptors
.filter { !(it is DeclarationDescriptorWithVisibility) || Visibilities.isVisible(it, scope.getContainingDeclaration()) }
- .map({toLookupElement(it)}) //TODO: use ::toLookupElement but it causes compiler to crash
+ .map(::toLookupElement)
+}
+
+private fun mergeTails(tails: Collection): Tail? {
+ if (tails.size == 1) return tails.single()
+ return if (HashSet(tails).size == 1) tails.first() else null
+}
+
+private fun decorateLookupElement(lookupElement: LookupElement, tail: Tail?): LookupElement {
+ return when (tail) {
+ null -> lookupElement
+
+ Tail.COMMA -> object: LookupElementDecorator(lookupElement) {
+ override fun handleInsert(context: InsertionContext) {
+ WithCommaInsertHandler.handleInsert(context, lookupElement)
+ }
+ }
+
+ else -> throw RuntimeException("Unknown tail type")
+ }
}
private fun isSubtypeOf(t: JetType, expectedType: JetType): Boolean{
diff --git a/idea/src/org/jetbrains/jet/plugin/completion/WithCommaInsertHandler.kt b/idea/src/org/jetbrains/jet/plugin/completion/WithCommaInsertHandler.kt
new file mode 100644
index 00000000000..767094671c7
--- /dev/null
+++ b/idea/src/org/jetbrains/jet/plugin/completion/WithCommaInsertHandler.kt
@@ -0,0 +1,50 @@
+package org.jetbrains.jet.plugin.completion
+
+import com.intellij.codeInsight.lookup.*
+import com.intellij.codeInsight.completion.*
+import com.intellij.openapi.editor.event.DocumentListener
+import com.intellij.openapi.editor.event.DocumentEvent
+import com.intellij.openapi.util.TextRange
+
+object WithCommaInsertHandler : InsertHandler {
+ override fun handleInsert(context: InsertionContext, item: LookupElement) {
+ val document = context.getDocument()
+ val caretModel = context.getEditor().getCaretModel()
+
+ var maxChangeOffset = caretModel.getOffset()
+ val documentListener = object: DocumentListener {
+ override fun documentChanged(event: DocumentEvent) {
+ val oldEndOffset = event.getOffset() + event.getOldLength()
+ if (oldEndOffset < maxChangeOffset) {
+ maxChangeOffset += event.getNewLength() - event.getOldLength()
+ }
+ else {
+ maxChangeOffset = event.getOffset() + event.getNewLength()
+ }
+ }
+
+ override fun beforeDocumentChange(event: DocumentEvent) {
+ }
+ }
+
+ document.addDocumentListener(documentListener)
+ item.handleInsert(context)
+ document.removeDocumentListener(documentListener)
+
+ val moveCaret = caretModel.getOffset() == maxChangeOffset
+
+ if (maxChangeOffset < document.getTextLength() && document.getText(TextRange(maxChangeOffset, maxChangeOffset + 1)) == ",") {
+ document.deleteString(maxChangeOffset, maxChangeOffset + 1)
+
+ if (maxChangeOffset < document.getTextLength() && document.getText(TextRange(maxChangeOffset, maxChangeOffset + 1)) == " ") {
+ document.deleteString(maxChangeOffset, maxChangeOffset + 1)
+ }
+ }
+
+ val textToInsert = ", " //TODO: code style option
+ document.insertString(maxChangeOffset, textToInsert)
+ if (moveCaret) {
+ caretModel.moveToOffset(maxChangeOffset + textToInsert.length)
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/completion/handlers/smart/Comma1.kt b/idea/testData/completion/handlers/smart/Comma1.kt
new file mode 100644
index 00000000000..eed859fab46
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma1.kt
@@ -0,0 +1,7 @@
+fun foo(s: String){ }
+
+fun bar(sss: String) {
+ foo()
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma1.kt.after b/idea/testData/completion/handlers/smart/Comma1.kt.after
new file mode 100644
index 00000000000..0fbbf45ca01
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma1.kt.after
@@ -0,0 +1,7 @@
+fun foo(s: String){ }
+
+fun bar(sss: String) {
+ foo(sss)
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma2.kt b/idea/testData/completion/handlers/smart/Comma2.kt
new file mode 100644
index 00000000000..92af737c80c
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma2.kt
@@ -0,0 +1,7 @@
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo()
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma2.kt.after b/idea/testData/completion/handlers/smart/Comma2.kt.after
new file mode 100644
index 00000000000..b7d3f1195f7
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma2.kt.after
@@ -0,0 +1,7 @@
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo(sss, )
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma3.kt b/idea/testData/completion/handlers/smart/Comma3.kt
new file mode 100644
index 00000000000..63aed0b75f3
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma3.kt
@@ -0,0 +1,8 @@
+fun foo(s: String){ }
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo()
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma3.kt.after b/idea/testData/completion/handlers/smart/Comma3.kt.after
new file mode 100644
index 00000000000..819db9a033f
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma3.kt.after
@@ -0,0 +1,8 @@
+fun foo(s: String){ }
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo(sss)
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma4.kt b/idea/testData/completion/handlers/smart/Comma4.kt
new file mode 100644
index 00000000000..6eb3da91360
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma4.kt
@@ -0,0 +1,8 @@
+fun foo(o: Any){ }
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo()
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma4.kt.after b/idea/testData/completion/handlers/smart/Comma4.kt.after
new file mode 100644
index 00000000000..1963ba477aa
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma4.kt.after
@@ -0,0 +1,8 @@
+fun foo(o: Any){ }
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo(sss)
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma5.kt b/idea/testData/completion/handlers/smart/Comma5.kt
new file mode 100644
index 00000000000..82d6149f297
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma5.kt
@@ -0,0 +1,8 @@
+fun foo(o: Any, c: Char){ }
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo()
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma5.kt.after b/idea/testData/completion/handlers/smart/Comma5.kt.after
new file mode 100644
index 00000000000..6aee486f6de
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma5.kt.after
@@ -0,0 +1,8 @@
+fun foo(o: Any, c: Char){ }
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo(sss, )
+}
+
+//ELEMENT: sss
diff --git a/idea/testData/completion/handlers/smart/Comma6.kt b/idea/testData/completion/handlers/smart/Comma6.kt
new file mode 100644
index 00000000000..4acee9b73f1
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma6.kt
@@ -0,0 +1,9 @@
+fun foo(s: String, i: Int){ }
+
+fun bar() {
+ foo()
+}
+
+fun getString(p: Int): String = ""
+
+//ELEMENT: getString
diff --git a/idea/testData/completion/handlers/smart/Comma6.kt.after b/idea/testData/completion/handlers/smart/Comma6.kt.after
new file mode 100644
index 00000000000..983ab204ae8
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma6.kt.after
@@ -0,0 +1,9 @@
+fun foo(s: String, i: Int){ }
+
+fun bar() {
+ foo(getString(), )
+}
+
+fun getString(p: Int): String = ""
+
+//ELEMENT: getString
diff --git a/idea/testData/completion/handlers/smart/Comma7.kt b/idea/testData/completion/handlers/smart/Comma7.kt
new file mode 100644
index 00000000000..76f709e1c7b
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma7.kt
@@ -0,0 +1,9 @@
+fun foo(c: C, i: Int){ }
+
+class C {
+ fun bar() {
+ foo()
+ }
+}
+
+//ELEMENT: this
diff --git a/idea/testData/completion/handlers/smart/Comma7.kt.after b/idea/testData/completion/handlers/smart/Comma7.kt.after
new file mode 100644
index 00000000000..c827e9e7cb4
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma7.kt.after
@@ -0,0 +1,9 @@
+fun foo(c: C, i: Int){ }
+
+class C {
+ fun bar() {
+ foo(this, )
+ }
+}
+
+//ELEMENT: this
diff --git a/idea/testData/completion/handlers/smart/Comma8.kt b/idea/testData/completion/handlers/smart/Comma8.kt
new file mode 100644
index 00000000000..66b472a1c2b
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma8.kt
@@ -0,0 +1,9 @@
+trait Foo
+
+fun foo(f: Foo, i: Int){}
+
+fun bar() {
+ foo()
+}
+
+//ELEMENT: object
diff --git a/idea/testData/completion/handlers/smart/Comma8.kt.after b/idea/testData/completion/handlers/smart/Comma8.kt.after
new file mode 100644
index 00000000000..e4b1f6a014e
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma8.kt.after
@@ -0,0 +1,9 @@
+trait Foo
+
+fun foo(f: Foo, i: Int){}
+
+fun bar() {
+ foo(object: Foo {}, )
+}
+
+//ELEMENT: object
diff --git a/idea/testData/completion/handlers/smart/Comma9.kt b/idea/testData/completion/handlers/smart/Comma9.kt
new file mode 100644
index 00000000000..6392cdca7ca
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma9.kt
@@ -0,0 +1,12 @@
+enum class Foo {
+ X
+ Y
+}
+
+fun foo(f: Foo, i: Int){}
+
+fun bar(){
+ foo()
+}
+
+// ELEMENT: Foo.X
diff --git a/idea/testData/completion/handlers/smart/Comma9.kt.after b/idea/testData/completion/handlers/smart/Comma9.kt.after
new file mode 100644
index 00000000000..3ffc41649c8
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/Comma9.kt.after
@@ -0,0 +1,12 @@
+enum class Foo {
+ X
+ Y
+}
+
+fun foo(f: Foo, i: Int){}
+
+fun bar(){
+ foo(Foo.X, )
+}
+
+// ELEMENT: Foo.X
diff --git a/idea/testData/completion/handlers/smart/TabReplaceComma1.kt b/idea/testData/completion/handlers/smart/TabReplaceComma1.kt
new file mode 100644
index 00000000000..d6587ab4008
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/TabReplaceComma1.kt
@@ -0,0 +1,8 @@
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo(a, b)
+}
+
+//ELEMENT: sss
+//CHAR: \t
diff --git a/idea/testData/completion/handlers/smart/TabReplaceComma1.kt.after b/idea/testData/completion/handlers/smart/TabReplaceComma1.kt.after
new file mode 100644
index 00000000000..5c4cb9da176
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/TabReplaceComma1.kt.after
@@ -0,0 +1,8 @@
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo(sss, b)
+}
+
+//ELEMENT: sss
+//CHAR: \t
diff --git a/idea/testData/completion/handlers/smart/TabReplaceComma2.kt b/idea/testData/completion/handlers/smart/TabReplaceComma2.kt
new file mode 100644
index 00000000000..4452b12e07b
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/TabReplaceComma2.kt
@@ -0,0 +1,8 @@
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo(a,b)
+}
+
+//ELEMENT: sss
+//CHAR: \t
diff --git a/idea/testData/completion/handlers/smart/TabReplaceComma2.kt.after b/idea/testData/completion/handlers/smart/TabReplaceComma2.kt.after
new file mode 100644
index 00000000000..5c4cb9da176
--- /dev/null
+++ b/idea/testData/completion/handlers/smart/TabReplaceComma2.kt.after
@@ -0,0 +1,8 @@
+fun foo(s: String, i: Int){ }
+
+fun bar(sss: String) {
+ foo(sss, b)
+}
+
+//ELEMENT: sss
+//CHAR: \t
diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java
index 2ac96b350cb..43a61bd5774 100644
--- a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java
+++ b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java
@@ -66,6 +66,51 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest("idea/testData/completion/handlers/smart/ClassObjectMethod2.kt");
}
+ @TestMetadata("Comma1.kt")
+ public void testComma1() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/Comma1.kt");
+ }
+
+ @TestMetadata("Comma2.kt")
+ public void testComma2() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/Comma2.kt");
+ }
+
+ @TestMetadata("Comma3.kt")
+ public void testComma3() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/Comma3.kt");
+ }
+
+ @TestMetadata("Comma4.kt")
+ public void testComma4() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/Comma4.kt");
+ }
+
+ @TestMetadata("Comma5.kt")
+ public void testComma5() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/Comma5.kt");
+ }
+
+ @TestMetadata("Comma6.kt")
+ public void testComma6() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/Comma6.kt");
+ }
+
+ @TestMetadata("Comma7.kt")
+ public void testComma7() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/Comma7.kt");
+ }
+
+ @TestMetadata("Comma8.kt")
+ public void testComma8() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/Comma8.kt");
+ }
+
+ @TestMetadata("Comma9.kt")
+ public void testComma9() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/Comma9.kt");
+ }
+
@TestMetadata("Constructor.kt")
public void testConstructor() throws Exception {
doTest("idea/testData/completion/handlers/smart/Constructor.kt");
@@ -131,6 +176,16 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest("idea/testData/completion/handlers/smart/JavaStaticMethodInsertsImport.kt");
}
+ @TestMetadata("TabReplaceComma1.kt")
+ public void testTabReplaceComma1() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/TabReplaceComma1.kt");
+ }
+
+ @TestMetadata("TabReplaceComma2.kt")
+ public void testTabReplaceComma2() throws Exception {
+ doTest("idea/testData/completion/handlers/smart/TabReplaceComma2.kt");
+ }
+
@TestMetadata("TabReplaceExpression.kt")
public void testTabReplaceExpression() throws Exception {
doTest("idea/testData/completion/handlers/smart/TabReplaceExpression.kt");