diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetCallExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetCallExpression.java index 4ec8e3e0efd..b3109997bae 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetCallExpression.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetCallExpression.java @@ -54,6 +54,11 @@ public class JetCallExpression extends JetReferenceExpression implements JetCall return (JetTypeArgumentList) findChildByType(JetNodeTypes.TYPE_ARGUMENT_LIST); } + /** + * Normally there should be only one (or zero) function literal arguments. + * The returned value is a list for better handling of commonly made mistake of a function taking a lambda and returning another function. + * Most of users can simply ignore lists of more than one element. + */ @Override @NotNull public List getFunctionLiteralArguments() { diff --git a/idea/src/org/jetbrains/jet/plugin/completion/DescriptorLookupConverter.java b/idea/src/org/jetbrains/jet/plugin/completion/DescriptorLookupConverter.java index e4fbb98d012..f7f65b10669 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/DescriptorLookupConverter.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/DescriptorLookupConverter.java @@ -37,14 +37,11 @@ import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.plugin.JetDescriptorIconProvider; -import org.jetbrains.jet.plugin.completion.handlers.JetClassInsertHandler; -import org.jetbrains.jet.plugin.completion.handlers.JetJavaClassInsertHandler; +import org.jetbrains.jet.plugin.completion.handlers.*; import org.jetbrains.jet.renderer.DescriptorRenderer; import java.util.List; -import static org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler.*; - public final class DescriptorLookupConverter { private DescriptorLookupConverter() {} @@ -91,7 +88,11 @@ public final class DescriptorLookupConverter { typeText = DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor); } - element = element.withInsertHandler(getInsertHandler(descriptor)); + InsertHandler insertHandler = getDefaultInsertHandler(descriptor); + element = element.withInsertHandler(insertHandler); + if (insertHandler instanceof JetFunctionInsertHandler && ((JetFunctionInsertHandler)insertHandler).getLambdaInfo() != null) { + element.putUserData(JetCompletionCharFilter.ACCEPT_OPENING_BRACE, true); + } element = element.withTailText(tailText, true).withTypeText(typeText).withPresentableText(presentableText); element = element.withIcon(JetDescriptorIconProvider.getIcon(descriptor, Iconable.ICON_FLAG_VISIBILITY)); element = element.withStrikeoutness(KotlinBuiltIns.getInstance().isDeprecated(descriptor)); @@ -100,20 +101,26 @@ public final class DescriptorLookupConverter { } @Nullable - public static InsertHandler getInsertHandler(@NotNull DeclarationDescriptor descriptor) { + public static InsertHandler getDefaultInsertHandler(@NotNull DeclarationDescriptor descriptor) { if (descriptor instanceof FunctionDescriptor) { FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; + List parameters = functionDescriptor.getValueParameters(); - if (functionDescriptor.getValueParameters().isEmpty()) { - return EMPTY_FUNCTION_HANDLER; + if (parameters.isEmpty()) { + return JetFunctionInsertHandler.NO_PARAMETERS_HANDLER; } - if (functionDescriptor.getValueParameters().size() == 1 && - KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(functionDescriptor.getValueParameters().get(0).getType())) { - return PARAMS_BRACES_FUNCTION_HANDLER; + if (parameters.size() == 1) { + JetType parameterType = parameters.get(0).getType(); + if (KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(parameterType)) { + int parameterCount = KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(parameterType).size(); + if (parameterCount <= 1) { // otherwise additional item with lambda template is to be added + return new JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, new GenerateLambdaInfo(parameterType, false)); + } + } } - return PARAMS_PARENTHESIS_FUNCTION_HANDLER; + return JetFunctionInsertHandler.WITH_PARAMETERS_HANDLER; } if (descriptor instanceof ClassDescriptor) { @@ -136,7 +143,7 @@ public final class DescriptorLookupConverter { PsiClass containingClass = ((PsiMember) declaration).getContainingClass(); if (containingClass != null && !JavaResolverPsiUtils.isCompiledKotlinClassOrPackageClass(containingClass)) { if (declaration instanceof PsiMethod) { - InsertHandler handler = getInsertHandler(descriptor); + InsertHandler handler = getDefaultInsertHandler(descriptor); assert handler != null: "Special kotlin handler is expected for function: " + declaration.getText() + " and descriptor: " + DescriptorRenderer.FQNAMES_IN_TYPES.render(descriptor); diff --git a/idea/src/org/jetbrains/jet/plugin/completion/smart/ExpectedInfos.kt b/idea/src/org/jetbrains/jet/plugin/completion/ExpectedInfos.kt similarity index 68% rename from idea/src/org/jetbrains/jet/plugin/completion/smart/ExpectedInfos.kt rename to idea/src/org/jetbrains/jet/plugin/completion/ExpectedInfos.kt index 94d64a647ac..aa725be2fa3 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/smart/ExpectedInfos.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/ExpectedInfos.kt @@ -1,4 +1,4 @@ -package org.jetbrains.jet.plugin.completion.smart +package org.jetbrains.jet.plugin.completion import org.jetbrains.jet.lang.psi.JetExpression import org.jetbrains.jet.lang.resolve.BindingContext @@ -23,17 +23,25 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor import java.util.HashSet import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall import org.jetbrains.jet.lang.descriptors.ModuleDescriptor +import org.jetbrains.jet.lang.types.JetType + +enum class Tail { + COMMA + PARENTHESIS +} + +data class ExpectedInfo(val `type`: JetType, val tail: Tail?) class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: ModuleDescriptor) { public fun calculate(expressionWithType: JetExpression): Collection? { - val expectedInfos = calculateForArgument(expressionWithType) - if (expectedInfos != null) { - return expectedInfos - } - else { - val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null - return listOf(ExpectedInfo(expectedType, null)) - } + val expectedInfos1 = calculateForArgument(expressionWithType) + if (expectedInfos1 != null) return expectedInfos1 + + val expectedInfos2 = calculateForFunctionLiteralArgument(expressionWithType) + if (expectedInfos2 != null) return expectedInfos2 + + val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null + return listOf(ExpectedInfo(expectedType, null)) } private fun calculateForArgument(expressionWithType: JetExpression): Collection? { @@ -42,6 +50,21 @@ class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: Mo val argumentList = argument.getParent() as JetValueArgumentList val argumentIndex = argumentList.getArguments().indexOf(argument) val callExpression = argumentList.getParent() as? JetCallExpression ?: return null + return calculateForArgument(callExpression, argumentIndex, false) + } + + private fun calculateForFunctionLiteralArgument(expressionWithType: JetExpression): Collection? { + val callExpression = expressionWithType.getParent() as? JetCallExpression + if (callExpression != null) { + val arguments = callExpression.getFunctionLiteralArguments() + if (arguments.size > 0 && arguments[0] == expressionWithType) { + return calculateForArgument(callExpression, callExpression.getValueArguments().size, true) + } + } + return null + } + + private fun calculateForArgument(callExpression: JetCallExpression, argumentIndex: Int, isFunctionLiteralArgument: Boolean): Collection? { val calleeExpression = callExpression.getCalleeExpression() val parent = callExpression.getParent() @@ -62,7 +85,7 @@ class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: Mo val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, calleeExpression] ?: return null //TODO: discuss it val callResolutionContext = BasicCallResolutionContext.create( - DelegatingBindingTrace(bindingContext, "Temporary trace for smart completion"), + DelegatingBindingTrace(bindingContext, "Temporary trace for completion"), resolutionScope, call, bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression] ?: TypeUtils.NO_EXPECTED_TYPE, @@ -71,15 +94,20 @@ class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: Mo CheckValueArgumentsMode.ENABLED, CompositeExtension(listOf()), false).replaceCollectAllCandidates(true) - val callResolver = InjectorForMacros(expressionWithType.getProject(), moduleDescriptor).getCallResolver()!! + val callResolver = InjectorForMacros(callExpression.getProject(), moduleDescriptor).getCallResolver()!! val results: OverloadResolutionResults = callResolver.resolveFunctionCall(callResolutionContext) val expectedInfos = HashSet() for (candidate: ResolvedCall in results.getAllCandidates()!!) { val parameters = candidate.getResultingDescriptor().getValueParameters() - if (parameters.size <= argumentIndex) continue + if (isFunctionLiteralArgument) { + if (argumentIndex != parameters.size - 1) continue + } + else{ + if (parameters.size <= argumentIndex) continue + } val parameterDescriptor = parameters[argumentIndex] - val tail = if (argumentIndex == parameters.size - 1) Tail.PARENTHESIS else Tail.COMMA + val tail = if (isFunctionLiteralArgument) null else if (argumentIndex == parameters.size - 1) Tail.PARENTHESIS else Tail.COMMA expectedInfos.add(ExpectedInfo(parameterDescriptor.getType(), tail)) } return expectedInfos diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionCharFilter.kt b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionCharFilter.kt index ad77641c1a7..0330a03b763 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionCharFilter.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionCharFilter.kt @@ -21,55 +21,37 @@ import com.intellij.codeInsight.lookup.Lookup import com.intellij.codeInsight.lookup.CharFilter.Result import org.jetbrains.jet.lang.psi.JetFile import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler +import com.intellij.openapi.util.Key public open class JetCompletionCharFilter() : CharFilter() { + class object { + public val ACCEPT_OPENING_BRACE: Key = Key("JetCompletionCharFilter.ACCEPT_OPENNING_BRACE") + } + public override fun acceptChar(c : Char, prefixLength : Int, lookup : Lookup) : Result? { if (lookup.getPsiFile() !is JetFile) return null - fun checkHideLookupForRangeOperator(): Result? { - if (c == '.' && prefixLength == 0 && !lookup.isSelectionTouched()) { - val caret = lookup.getEditor().getCaretModel().getOffset() - if (caret > 0 && (lookup.getEditor().getDocument().getCharsSequence().charAt(caret - 1)) == '.') { - return Result.HIDE_LOOKUP - } + if (c == '.' && prefixLength == 0 && !lookup.isSelectionTouched()) { + val caret = lookup.getEditor().getCaretModel().getOffset() + if (caret > 0 && (lookup.getEditor().getDocument().getCharsSequence().charAt(caret - 1)) == '.') { + return Result.HIDE_LOOKUP } - - return null } - fun checkFinishCompletionForOpenBrace(): Result? { - if (c == '{') { - val currentItem = lookup.getCurrentItem() - if (currentItem != null && (currentItem.getObject() is JetLookupObject)) { - val lookupObject = (currentItem.getObject() as JetLookupObject) - val descriptor = lookupObject.getDescriptor() - - if (descriptor != null) { - val handler = DescriptorLookupConverter.getInsertHandler(descriptor) - if (handler == JetFunctionInsertHandler.PARAMS_BRACES_FUNCTION_HANDLER) { - return Result.SELECT_ITEM_AND_FINISH_LOOKUP - } - } - } + if (c == '{') { + val currentItem = lookup.getCurrentItem() + if (currentItem != null && currentItem.getUserData(ACCEPT_OPENING_BRACE) ?: false) { + return Result.SELECT_ITEM_AND_FINISH_LOOKUP } - - return null } - fun checkFinishCompletionForEqual(): Result? { - if (c == '=') { - val currentItem = lookup.getCurrentItem() - if (currentItem != null && (currentItem.getObject() is KotlinNamedParametersContributor.NamedParameterLookupObject)) { - return Result.SELECT_ITEM_AND_FINISH_LOOKUP - } + if (c == '=') { + val currentItem = lookup.getCurrentItem() + if (currentItem != null && (currentItem.getObject() is KotlinNamedParametersContributor.NamedParameterLookupObject)) { + return Result.SELECT_ITEM_AND_FINISH_LOOKUP } - - return null } - return checkHideLookupForRangeOperator() ?: - checkFinishCompletionForOpenBrace() ?: - checkFinishCompletionForEqual() ?: - null + return null } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionResultSet.java b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionResultSet.java index 2a027273caf..0d8af56027f 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionResultSet.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionResultSet.java @@ -17,14 +17,27 @@ package org.jetbrains.jet.plugin.completion; import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementDecorator; +import com.intellij.codeInsight.lookup.LookupElementPresentation; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import org.jetbrains.jet.plugin.completion.handlers.CaretPosition; +import org.jetbrains.jet.plugin.completion.handlers.GenerateLambdaInfo; +import org.jetbrains.jet.plugin.completion.handlers.HandlersPackage; +import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler; import org.jetbrains.jet.plugin.project.ResolveSessionForBodies; +import java.util.List; + public class JetCompletionResultSet { private final ResolveSessionForBodies resolveSession; private final BindingContext bindingContext; @@ -74,12 +87,38 @@ public class JetCompletionResultSet { } addElement(DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor)); + + // add special item for function with one argument of function type with more than one parameter + if (descriptor instanceof FunctionDescriptor) { + FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; + List parameters = functionDescriptor.getValueParameters(); + if (parameters.size() == 1) { + final JetType parameterType = parameters.get(0).getType(); + if (KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(parameterType)) { + int parameterCount = KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(parameterType).size(); + if (parameterCount > 1) { + LookupElement lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor); + addElement(new LookupElementDecorator(lookupElement) { + @Override + public void renderElement(LookupElementPresentation presentation) { + super.renderElement(presentation); + presentation.setItemText(getLookupString() + " " + HandlersPackage.buildLambdaPresentation(parameterType)); + } + + @Override + public void handleInsert(InsertionContext context) { + new JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, new GenerateLambdaInfo(parameterType, true)) + .handleInsert(context, this); + } + }); + } + } + } + } } public void addElement(@NotNull LookupElement element) { - if (!result.getPrefixMatcher().prefixMatches(element)) { - return; - } + if (!result.getPrefixMatcher().prefixMatches(element)) return; result.addElement(element); isSomethingAdded = true; diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.kt b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.kt index e3609c2c2cc..a0b5e4e2630 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.kt @@ -279,7 +279,7 @@ public open class JetKeywordCompletionContributor() : CompletionContributor() { lookupElementBuilder.withInsertHandler(JetKeywordCompletionContributor.KEYWORDS_INSERT_HANDLER) } else { - lookupElementBuilder.withInsertHandler(JetFunctionInsertHandler.EMPTY_FUNCTION_HANDLER) + lookupElementBuilder.withInsertHandler(JetFunctionInsertHandler.NO_PARAMETERS_HANDLER) } } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/GenerateLambda.kt b/idea/src/org/jetbrains/jet/plugin/completion/handlers/GenerateLambda.kt new file mode 100644 index 00000000000..15b20500ef8 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/GenerateLambda.kt @@ -0,0 +1,128 @@ +package org.jetbrains.jet.plugin.completion.handlers + +import com.intellij.openapi.command.CommandProcessor +import com.intellij.codeInsight.completion.InsertionContext +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.util.Computable +import com.intellij.codeInsight.template.TemplateManager +import com.intellij.openapi.util.TextRange +import com.intellij.codeInsight.template.Template +import org.jetbrains.jet.plugin.refactoring.JetNameValidator +import org.jetbrains.jet.plugin.refactoring.JetNameSuggester +import org.jetbrains.jet.renderer.DescriptorRenderer +import com.intellij.codeInsight.template.Expression +import com.intellij.codeInsight.template.ExpressionContext +import com.intellij.codeInsight.template.TextResult +import com.intellij.codeInsight.template.Result +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementBuilder +import org.jetbrains.jet.lang.types.JetType +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.jet.lang.psi.JetExpression +import org.jetbrains.jet.plugin.completion.ExpectedInfos +import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache +import org.jetbrains.jet.lang.psi.JetFile + +fun insertLambdaTemplate(context: InsertionContext, placeholderRange: TextRange, lambdaType: JetType) { + val explicitParameterTypes = needExplicitParameterTypes(context, placeholderRange, lambdaType) + + // we start template later to not interfere with insertion of tail type + val commandProcessor = CommandProcessor.getInstance() + val commandName = commandProcessor.getCurrentCommandName() + val commandGroupId = commandProcessor.getCurrentCommandGroupId() + + val rangeMarker = context.getDocument().createRangeMarker(placeholderRange) + + context.setLaterRunnable { + commandProcessor.executeCommand(context.getProject(), { + ApplicationManager.getApplication()!!.runWriteAction(Computable { + try { + if (rangeMarker.isValid()) { + context.getDocument().deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset()) + context.getEditor().getCaretModel().moveToOffset(rangeMarker.getStartOffset()) + val template = buildTemplate(lambdaType, explicitParameterTypes, context.getProject()) + TemplateManager.getInstance(context.getProject()).startTemplate(context.getEditor(), template) + } + } + finally { + rangeMarker.dispose() + } + }) + }, commandName, commandGroupId) + } +} + +fun buildLambdaPresentation(lambdaType: JetType): String { + val parameterTypes = functionParameterTypes(lambdaType) + val parametersPresentation = parameterTypes.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it) }.makeString(", ") + fun wrap(s: String) = if (parameterTypes.size != 1) "($s)" else s + return "{ ${wrap(parametersPresentation)} -> ... }" +} + +private fun needExplicitParameterTypes(context: InsertionContext, placeholderRange: TextRange, lambdaType: JetType): Boolean { + PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments() + val file = context.getFile() as JetFile + val expression = PsiTreeUtil.findElementOfClassAtRange(file, placeholderRange.getStartOffset(), placeholderRange.getEndOffset(), javaClass()) + if (expression == null) return false + + val resolveSession = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(file) + val bindingContext = resolveSession.resolveToElement(expression) + val expectedInfos = ExpectedInfos(bindingContext, resolveSession.getModuleDescriptor()).calculate(expression) ?: return false + val functionTypes = expectedInfos.map { it.`type` }.filter { KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(it) }.toSet() + if (functionTypes.size <= 1) return false + + val lambdaParameterCount = KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(lambdaType).size + return functionTypes.filter { KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(it).size == lambdaParameterCount}.size > 1 +} + +private fun buildTemplate(lambdaType: JetType, explicitParameterTypes: Boolean, project: Project): Template { + val parameterTypes = functionParameterTypes(lambdaType) + + val nameValidator = JetNameValidator.getEmptyValidator(project) //TODO: check for names in scope + + val useParenthesis = explicitParameterTypes || parameterTypes.size != 1 + + val manager = TemplateManager.getInstance(project) + + val template = manager.createTemplate("", "") + template.setToShortenLongNames(true) + //template.setToReformat(true) //TODO + template.addTextSegment("{ ") + if (useParenthesis) { + template.addTextSegment("(") + } + + for (i in parameterTypes.indices) { + val parameterType = parameterTypes[i] + if (i > 0) { + template.addTextSegment(", ") + } + template.addVariable(ParameterNameExpression(JetNameSuggester.suggestNames(parameterType, nameValidator, "p")), true) + if (explicitParameterTypes) { + template.addTextSegment(": " + DescriptorRenderer.SOURCE_CODE.renderType(parameterType)) + } + } + + if (useParenthesis) { + template.addTextSegment(")") + } + template.addTextSegment(" -> ") + template.addEndVariable() + template.addTextSegment(" }") + return template +} + +private class ParameterNameExpression(val nameSuggestions: Array) : Expression() { + override fun calculateResult(context: ExpressionContext?) = TextResult(nameSuggestions[0]) + + override fun calculateQuickResult(context: ExpressionContext?): Result? = null + + override fun calculateLookupItems(context: ExpressionContext?) + = Array(nameSuggestions.size, { LookupElementBuilder.create(nameSuggestions[it]) }) +} + +fun functionParameterTypes(functionType: JetType): List + = KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(functionType).map { it.getType() } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.kt b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.kt index e8eeb3bb56f..a823d619404 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.kt @@ -33,22 +33,22 @@ import org.jetbrains.jet.plugin.completion.JetLookupObject import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor import org.jetbrains.jet.lang.psi.JetQualifiedExpression import org.jetbrains.jet.lang.resolve.DescriptorUtils -import org.jetbrains.jet.lang.resolve.name.FqName import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper +import com.intellij.openapi.editor.Document +import org.jetbrains.jet.lang.types.JetType +import com.intellij.openapi.util.TextRange public enum class CaretPosition { IN_BRACKETS AFTER_BRACKETS } -public enum class BracketType { - PARENTHESIS - BRACES -} -public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val bracketType : BracketType) : InsertHandler { +public data class GenerateLambdaInfo(val lambdaType: JetType, val explicitParameters: Boolean) + +public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lambdaInfo: GenerateLambdaInfo?) : InsertHandler { { - if (caretPosition == CaretPosition.AFTER_BRACKETS && bracketType == BracketType.BRACES) { - throw IllegalArgumentException("CaretPosition.AFTER_BRACKETS with bracketType == BracketType.BRACES combination is not supported") + if (caretPosition == CaretPosition.AFTER_BRACKETS && lambdaInfo != null) { + throw IllegalArgumentException("CaretPosition.AFTER_BRACKETS with lambdaInfo != null combination is not supported") } } @@ -67,32 +67,29 @@ public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val bra addBrackets(context, element) } - addImport(context, item) + addImports(context, item) } private fun addBrackets(context : InsertionContext, offsetElement : PsiElement) { - val offset = context.getSelectionEndOffset() + val offset = context.getTailOffset() val document = context.getDocument() val completionChar = context.getCompletionChar() - var documentText = document.getText() - - val forceParenthesis = bracketType == BracketType.BRACES && completionChar == '\t' && documentText.charAt(offset) == '(' - - val braces = bracketType == BracketType.BRACES && completionChar != '(' && !forceParenthesis + val forceParenthesis = lambdaInfo != null && completionChar == '\t' && document.getCharsSequence().charAt(offset) == '(' + val braces = lambdaInfo != null && completionChar != '(' && !forceParenthesis val openingBracket = if (braces) '{' else '(' val closingBracket = if (braces) '}' else ')' - var openingBracketIndex = indexOfSkippingSpace(documentText, openingBracket, offset) + var openingBracketOffset = indexOfSkippingSpace(document, openingBracket, offset) var inBracketsShift = 0 - if (openingBracketIndex == -1) { + if (openingBracketOffset == -1) { if (braces) { if (completionChar == ' ' || completionChar == '{') { context.setAddCompletionChar(false) } - if (isInsertSpacesInOneLineFunctionEnabled(offsetElement.getProject())) { + if (isInsertSpacesInOneLineFunctionEnabled(context.getProject())) { document.insertString(offset, " { }") inBracketsShift = 1 } @@ -104,74 +101,69 @@ public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val bra document.insertString(offset, "()") } PsiDocumentManager.getInstance(context.getProject()).commitDocument(document) - documentText = document.getText() } - openingBracketIndex = indexOfSkippingSpace(documentText, openingBracket, offset) - assert (openingBracketIndex != -1) { "If there wasn't open bracket it should already have been inserted" } + openingBracketOffset = indexOfSkippingSpace(document, openingBracket, offset) + assert (openingBracketOffset != -1) { "If there wasn't open bracket it should already have been inserted" } - val closeBracketIndex = indexOfSkippingSpace(documentText, closingBracket, openingBracketIndex + 1) + val closeBracketOffset = indexOfSkippingSpace(document, closingBracket, openingBracketOffset + 1) val editor = context.getEditor() var forcePlaceCaretIntoParentheses : Boolean = completionChar == '(' - if (caretPosition == CaretPosition.IN_BRACKETS || forcePlaceCaretIntoParentheses || closeBracketIndex == -1) { - editor.getCaretModel().moveToOffset(openingBracketIndex + 1 + inBracketsShift) + if (caretPosition == CaretPosition.IN_BRACKETS || forcePlaceCaretIntoParentheses || closeBracketOffset == -1) { + editor.getCaretModel().moveToOffset(openingBracketOffset + 1 + inBracketsShift) AutoPopupController.getInstance(context.getProject())?.autoPopupParameterInfo(editor, offsetElement) } else { - editor.getCaretModel().moveToOffset(closeBracketIndex + 1) + editor.getCaretModel().moveToOffset(closeBracketOffset + 1) } PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getDocument()) + + if (lambdaInfo != null && lambdaInfo.explicitParameters) { + insertLambdaTemplate(context, TextRange(openingBracketOffset, closeBracketOffset + 1), lambdaInfo.lambdaType) + } } class object { - public val EMPTY_FUNCTION_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.AFTER_BRACKETS, BracketType.PARENTHESIS) - public val PARAMS_PARENTHESIS_FUNCTION_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, BracketType.PARENTHESIS) - public val PARAMS_BRACES_FUNCTION_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, BracketType.BRACES) + public val NO_PARAMETERS_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.AFTER_BRACKETS, null) + public val WITH_PARAMETERS_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, null) private fun shouldAddBrackets(element : PsiElement) : Boolean { return PsiTreeUtil.getParentOfType(element, javaClass()) == null } - private fun indexOfSkippingSpace(str : String, ch : Char, startIndex : Int) : Int { - for (i in startIndex..str.length() - 1) { - val currentChar = str.charAt(i) - if (ch == currentChar) { - return i - } - - if (!Character.isWhitespace(currentChar)) { - return -1 - } + private fun indexOfSkippingSpace(document: Document, ch : Char, startIndex : Int) : Int { + val text = document.getCharsSequence() + for (i in startIndex..text.length() - 1) { + val currentChar = text.charAt(i) + if (ch == currentChar) return i + if (!Character.isWhitespace(currentChar)) return -1 } - return -1 } - private open fun isInsertSpacesInOneLineFunctionEnabled(project : Project) : Boolean { - val settings = CodeStyleSettingsManager.getSettings(project) - val jetSettings = settings.getCustomSettings(javaClass())!! - return jetSettings.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD - } + private open fun isInsertSpacesInOneLineFunctionEnabled(project : Project) + = CodeStyleSettingsManager.getSettings(project) + .getCustomSettings(javaClass())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD - private open fun addImport(context : InsertionContext, item : LookupElement) { + private open fun addImports(context : InsertionContext, item : LookupElement) { ApplicationManager.getApplication()?.runReadAction { () : Unit -> val startOffset = context.getStartOffset() val element = context.getFile().findElementAt(startOffset) if (element == null) return@runReadAction - if (context.getFile() is JetFile && item.getObject() is JetLookupObject) { + val file = context.getFile() + if (file is JetFile && item.getObject() is JetLookupObject) { val descriptor = (item.getObject() as JetLookupObject).getDescriptor() if (descriptor is SimpleFunctionDescriptor) { - val file = context.getFile() as JetFile val functionDescriptor = descriptor as SimpleFunctionDescriptor if (PsiTreeUtil.getParentOfType(element, javaClass()) != null && - functionDescriptor.getReceiverParameter() == null) { + functionDescriptor.getReceiverParameter() == null) { return@runReadAction } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandlers.kt b/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandlers.kt index 38c73c72604..221a0743e4e 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandlers.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandlers.kt @@ -3,7 +3,6 @@ package org.jetbrains.jet.plugin.completion.handlers import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.lookup.LookupElement import com.intellij.openapi.editor.event.* -import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiDocumentManager import com.intellij.codeInsight.AutoPopupController @@ -46,7 +45,7 @@ abstract class WithTailInsertHandlerBase : InsertHandler { class WithTailCharInsertHandler(val tailChar: Char, val spaceAfter: Boolean) : WithTailInsertHandlerBase() { override fun insertTail(context: InsertionContext, offset: Int, moveCaret: Boolean) { val document = context.getDocument() - fun isCharAt(offset: Int, c: Char) = offset < document.getTextLength() && document.getText(TextRange(offset, offset + 1))[0] == c + fun isCharAt(offset: Int, c: Char) = offset < document.getTextLength() && document.getCharsSequence().charAt(offset) == c if (isCharAt(offset, tailChar)) { document.deleteString(offset, offset + 1) 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 d8fe602681a..51a485e489e 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/smart/LambdaItems.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/LambdaItems.kt @@ -3,133 +3,45 @@ package org.jetbrains.jet.plugin.completion.smart import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementBuilder import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns -import org.jetbrains.jet.lang.types.JetType +import org.jetbrains.jet.plugin.completion.handlers.functionParameterTypes import org.jetbrains.jet.renderer.DescriptorRenderer -import org.jetbrains.jet.plugin.refactoring.JetNameValidator -import org.jetbrains.jet.plugin.refactoring.JetNameSuggester -import com.intellij.openapi.project.Project -import com.intellij.codeInsight.completion.InsertHandler -import com.intellij.codeInsight.completion.InsertionContext -import com.intellij.codeInsight.template.* -import com.intellij.openapi.command.CommandProcessor -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.util.Computable +import org.jetbrains.jet.plugin.completion.handlers.insertLambdaTemplate +import com.intellij.openapi.util.TextRange +import org.jetbrains.jet.plugin.completion.JetCompletionCharFilter +import org.jetbrains.jet.plugin.completion.ExpectedInfo +import org.jetbrains.jet.plugin.completion.handlers.buildLambdaPresentation -class LambdaItems(val project: Project) { +object LambdaItems { public fun addToCollection(collection: MutableCollection, functionExpectedInfos: Collection) { val distinctTypes = functionExpectedInfos.map { it.`type` }.toSet() - fun createLookupElement(lookupString: String, textBeforeCaret: String, textAfterCaret: String, shortenRefs: Boolean) - = LookupElementBuilder.create(lookupString) - .withInsertHandler(ArtificialElementInsertHandler(textBeforeCaret, textAfterCaret, shortenRefs)) - .suppressAutoInsertion() - val singleType = if (distinctTypes.size == 1) distinctTypes.single() else null val singleSignatureLength = singleType?.let { KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(it).size } val offerNoParametersLambda = singleSignatureLength == 0 || singleSignatureLength == 1 if (offerNoParametersLambda) { - val lookupElement = createLookupElement("{...}", "{ ", " }", shortenRefs = false) - collection.add(lookupElement.addTail(functionExpectedInfos)) + val lookupElement = LookupElementBuilder.create("{...}") + .withInsertHandler(ArtificialElementInsertHandler("{ ", " }", false)) + .suppressAutoInsertion() + .addTail(functionExpectedInfos) + lookupElement.putUserData(JetCompletionCharFilter.ACCEPT_OPENING_BRACE, true) + collection.add(lookupElement) } if (singleSignatureLength != 0) { for (functionType in distinctTypes) { - val parameterTypes = functionParameterTypes(functionType) - val parametersPresentation = parameterTypes.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it) }.makeString(", ") - val useExplicitTypes = distinctTypes.stream().any { it != functionType && functionParameterTypes(it).size == parameterTypes.size } - - fun wrap(s: String) = if (useParenthesis(useExplicitTypes, parameterTypes)) "($s)" else s - val lookupString = "{ ${wrap(parametersPresentation)} -> ... }" - + val lookupString = buildLambdaPresentation(functionType) val lookupElement = LookupElementBuilder.create(lookupString) - .withInsertHandler(LambdaInsertHandler(functionType, useExplicitTypes)) + .withInsertHandler({ (context, lookupElement) -> + val offset = context.getStartOffset() + val placeholder = "{}" + context.getDocument().replaceString(offset, context.getTailOffset(), placeholder) + insertLambdaTemplate(context, TextRange(offset, offset + placeholder.length), functionType) + }) .suppressAutoInsertion() - collection.add(lookupElement.addTail(functionExpectedInfos.filter { it.`type` == functionType })) + .addTail(functionExpectedInfos.filter { it.`type` == functionType }) + lookupElement.putUserData(JetCompletionCharFilter.ACCEPT_OPENING_BRACE, true) + collection.add(lookupElement) } } } - - - private fun useParenthesis(useExplicitTypes: Boolean, parameterTypes: List) = useExplicitTypes || parameterTypes.size != 1 - - private inner class LambdaInsertHandler(val functionType: JetType, val useExplicitTypes: Boolean) : InsertHandler { - override fun handleInsert(context: InsertionContext, item: LookupElement) { - val document = context.getDocument() - val editor = context.getEditor() - val offset = context.getStartOffset() - val placeholder = "{}" - document.replaceString(offset, context.getTailOffset(), placeholder) - val rangeMarker = document.createRangeMarker(offset, offset + placeholder.length) - - // we start template later to not interfere with insertion of tail type - val commandProcessor = CommandProcessor.getInstance() - val commandName = commandProcessor.getCurrentCommandName() - val commandGroupId = commandProcessor.getCurrentCommandGroupId() - context.setLaterRunnable { - commandProcessor.executeCommand(project, { - ApplicationManager.getApplication()!!.runWriteAction(Computable { - try { - if (rangeMarker.isValid()) { - document.deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset()) - editor.getCaretModel().moveToOffset(rangeMarker.getStartOffset()) - TemplateManager.getInstance(project).startTemplate(editor, buildTemplate()) - } - } - finally { - rangeMarker.dispose() - } - }) - }, commandName, commandGroupId) - } - } - - private fun buildTemplate(): Template { - val parameterTypes = functionParameterTypes(functionType) - - val nameValidator = JetNameValidator.getEmptyValidator(project) //TODO: check for names in scope - - val useParenthesis = useParenthesis(useExplicitTypes, parameterTypes) - - val manager = TemplateManager.getInstance(project) - - val template = manager.createTemplate("", "") - template.setToShortenLongNames(true) - //template.setToReformat(true) //TODO - template.addTextSegment("{ ") - if (useParenthesis) { - template.addTextSegment("(") - } - - for (i in parameterTypes.indices) { - val parameterType = parameterTypes[i] - if (i > 0) { - template.addTextSegment(", ") - } - template.addVariable(ParameterNameExpression(JetNameSuggester.suggestNames(parameterType, nameValidator, "p")), true) - if (useExplicitTypes) { - template.addTextSegment(": " + DescriptorRenderer.SOURCE_CODE.renderType(parameterType)) - } - } - - if (useParenthesis) { - template.addTextSegment(")") - } - template.addTextSegment(" -> ") - template.addEndVariable() - template.addTextSegment(" }") - return template - } - } - - private fun functionParameterTypes(functionType: JetType): List - = KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(functionType).map { it.getType() } - - private class ParameterNameExpression(val nameSuggestions: Array) : Expression() { - override fun calculateResult(context: ExpressionContext?) = TextResult(nameSuggestions[0]) - - override fun calculateQuickResult(context: ExpressionContext?): Result? = null - - override fun calculateLookupItems(context: ExpressionContext?) - = Array(nameSuggestions.size, { LookupElementBuilder.create(nameSuggestions[it]) }) - } } 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 dc64ee202e8..7b1f233026e 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt @@ -9,16 +9,9 @@ import org.jetbrains.jet.lang.types.* import com.intellij.codeInsight.lookup.* import com.intellij.codeInsight.completion.* import java.util.* -import org.jetbrains.jet.plugin.completion.DescriptorLookupConverter +import org.jetbrains.jet.plugin.completion.* import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns -enum class Tail { - COMMA - PARENTHESIS -} - -data class ExpectedInfo(val `type`: JetType, val tail: Tail?) - class SmartCompletion(val expression: JetSimpleNameExpression, val resolveSession: ResolveSessionForBodies, val visibilityFilter: (DeclarationDescriptor) -> Boolean) { @@ -78,7 +71,7 @@ class SmartCompletion(val expression: JetSimpleNameExpression, ThisItems(bindingContext).addToCollection(result, expressionWithType, expectedInfos) - LambdaItems(project).addToCollection(result, functionExpectedInfos) + LambdaItems.addToCollection(result, functionExpectedInfos) } return result 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 bf473e7da22..4366a75d228 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 org.jetbrains.jet.plugin.completion.ExpectedInfo import java.util.ArrayList // adds java static members, enum members and members from class object 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 c63b4c200e5..52aa5c7bc18 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/smart/ThisItems.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/ThisItems.kt @@ -18,6 +18,7 @@ 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 +import org.jetbrains.jet.plugin.completion.ExpectedInfo class ThisItems(val bindingContext: BindingContext) { public fun addToCollection(collection: MutableCollection, context: JetExpression, expectedInfos: Collection) { 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 8d49f6b4c83..482e3020ce4 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/smart/TypeInstantiationItems.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/TypeInstantiationItems.kt @@ -13,13 +13,13 @@ import org.jetbrains.jet.lang.descriptors.Modality import org.jetbrains.jet.lang.descriptors.ClassKind import org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor -import org.jetbrains.jet.plugin.completion.handlers.CaretPosition import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.codeInsight.completion.InsertionContext import org.jetbrains.jet.lang.resolve.BindingContext import org.jetbrains.jet.plugin.project.ResolveSessionForBodies -import com.intellij.codeInsight.AutoPopupController +import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler +import org.jetbrains.jet.plugin.completion.* class TypeInstantiationItems(val bindingContext: BindingContext, val resolveSession: ResolveSessionForBodies) { public fun addToCollection(collection: MutableCollection, expectedInfos: Collection) { @@ -65,26 +65,28 @@ class TypeInstantiationItems(val bindingContext: BindingContext, val resolveSess lookupElement = lookupElement.suppressAutoInsertion() } else { + //TODO: when constructor has one parameter of lambda type with more than one parameter, generate special additional item itemText += "()" - val constructors: Collection = classifier.getConstructors() - val caretPosition = + val constructors = classifier.getConstructors() + val baseInsertHandler = if (constructors.size == 0) - CaretPosition.AFTER_BRACKETS + JetFunctionInsertHandler.NO_PARAMETERS_HANDLER else if (constructors.size == 1) - if (constructors.first().getValueParameters().isEmpty()) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS + DescriptorLookupConverter.getDefaultInsertHandler(constructors.first())!! else - CaretPosition.IN_BRACKETS - insertHandler = InsertHandler {(context, item) -> - val editor = context.getEditor() - val startOffset = context.getStartOffset() - val text = typeText + "()" - editor.getDocument().replaceString(startOffset, context.getTailOffset(), text) - val endOffset = startOffset + text.length - editor.getCaretModel().moveToOffset(if (caretPosition == CaretPosition.IN_BRACKETS) endOffset - 1 else endOffset) + JetFunctionInsertHandler.WITH_PARAMETERS_HANDLER + insertHandler = object : InsertHandler { + override fun handleInsert(context: InsertionContext, item: LookupElement) { + context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), typeText) + context.setTailOffset(context.getStartOffset() + typeText.length) - shortenReferences(context, startOffset, endOffset) + baseInsertHandler.handleInsert(context, item) - AutoPopupController.getInstance(context.getProject())?.autoPopupParameterInfo(editor, null) + shortenReferences(context, context.getStartOffset(), context.getTailOffset()) + } + } + if ((baseInsertHandler as JetFunctionInsertHandler).lambdaInfo != null) { + lookupElement.putUserData(JetCompletionCharFilter.ACCEPT_OPENING_BRACE, true) } } 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 92e54f76610..f11aeef852b 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt @@ -17,6 +17,7 @@ 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 +import org.jetbrains.jet.plugin.completion.* class ArtificialElementInsertHandler( val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler{ diff --git a/idea/testData/completion/basic/common/HigherOrderFunction1.kt b/idea/testData/completion/basic/common/HigherOrderFunction1.kt new file mode 100644 index 00000000000..e3cec87323c --- /dev/null +++ b/idea/testData/completion/basic/common/HigherOrderFunction1.kt @@ -0,0 +1,8 @@ +fun foo(p: (String, Char) -> Unit){} + +fun test() { + fo +} + +// EXIST: { lookupString:"foo", itemText: "foo(p: (String, Char) -> Unit)", typeText:"Unit" } +// EXIST: { lookupString:"foo", itemText: "foo { (String, Char) -> ... }", typeText:"Unit" } diff --git a/idea/testData/completion/handlers/HigherOrderFunctionWithArgs1.kt b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs1.kt new file mode 100644 index 00000000000..00d5a177f5f --- /dev/null +++ b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs1.kt @@ -0,0 +1,5 @@ +fun foo(p : (String, Char) -> Boolean){} + +fun main(args: Array) { + fo +} diff --git a/idea/testData/completion/handlers/HigherOrderFunctionWithArgs1.kt.after b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs1.kt.after new file mode 100644 index 00000000000..97539c9cad4 --- /dev/null +++ b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs1.kt.after @@ -0,0 +1,5 @@ +fun foo(p : (String, Char) -> Boolean){} + +fun main(args: Array) { + foo { (s, c) -> } +} diff --git a/idea/testData/completion/handlers/HigherOrderFunctionWithArgs2.kt b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs2.kt new file mode 100644 index 00000000000..00d5a177f5f --- /dev/null +++ b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs2.kt @@ -0,0 +1,5 @@ +fun foo(p : (String, Char) -> Boolean){} + +fun main(args: Array) { + fo +} diff --git a/idea/testData/completion/handlers/HigherOrderFunctionWithArgs2.kt.after b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs2.kt.after new file mode 100644 index 00000000000..67549ef5cda --- /dev/null +++ b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs2.kt.after @@ -0,0 +1,5 @@ +fun foo(p : (String, Char) -> Boolean){} + +fun main(args: Array) { + foo() +} diff --git a/idea/testData/completion/handlers/HigherOrderFunctionWithArgs3.kt b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs3.kt new file mode 100644 index 00000000000..ed6199850d7 --- /dev/null +++ b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs3.kt @@ -0,0 +1,6 @@ +fun foo(p : (String, Char) -> Boolean){} +fun foo(p : (String, Boolean) -> Boolean){} + +fun main(args: Array) { + fo +} diff --git a/idea/testData/completion/handlers/HigherOrderFunctionWithArgs3.kt.after b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs3.kt.after new file mode 100644 index 00000000000..e41a63fb28a --- /dev/null +++ b/idea/testData/completion/handlers/HigherOrderFunctionWithArgs3.kt.after @@ -0,0 +1,6 @@ +fun foo(p : (String, Char) -> Boolean){} +fun foo(p : (String, Boolean) -> Boolean){} + +fun main(args: Array) { + foo { (s: String, c: Char) -> } +} diff --git a/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter1.kt b/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter1.kt new file mode 100644 index 00000000000..f629c2063a8 --- /dev/null +++ b/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter1.kt @@ -0,0 +1,3 @@ +class Foo(p : () -> Unit) + +var a : Foo = diff --git a/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter1.kt.after b/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter1.kt.after new file mode 100644 index 00000000000..131f9646282 --- /dev/null +++ b/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter1.kt.after @@ -0,0 +1,3 @@ +class Foo(p : () -> Unit) + +var a : Foo = Foo { } diff --git a/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter2.kt b/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter2.kt new file mode 100644 index 00000000000..fc72c5ec383 --- /dev/null +++ b/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter2.kt @@ -0,0 +1,3 @@ +class Foo(p : (String) -> Unit) + +var a : Foo = diff --git a/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter2.kt.after b/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter2.kt.after new file mode 100644 index 00000000000..f1fc2a112c0 --- /dev/null +++ b/idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter2.kt.after @@ -0,0 +1,3 @@ +class Foo(p : (String) -> Unit) + +var a : Foo = Foo { } diff --git a/idea/testData/completion/handlers/smart/LambdaInsertImport.kt b/idea/testData/completion/handlers/smart/LambdaInsertImport.kt index 859d73a97b0..4d254fdb198 100644 --- a/idea/testData/completion/handlers/smart/LambdaInsertImport.kt +++ b/idea/testData/completion/handlers/smart/LambdaInsertImport.kt @@ -5,4 +5,4 @@ fun bar() { foo() } -// ELEMENT: "{ (Date) -> ... }" +// ELEMENT: "{ Date -> ... }" diff --git a/idea/testData/completion/handlers/smart/LambdaInsertImport.kt.after b/idea/testData/completion/handlers/smart/LambdaInsertImport.kt.after index 3715f6d4194..66b5161f40a 100644 --- a/idea/testData/completion/handlers/smart/LambdaInsertImport.kt.after +++ b/idea/testData/completion/handlers/smart/LambdaInsertImport.kt.after @@ -7,4 +7,4 @@ fun bar() { foo({ (date: Date) -> }) } -// ELEMENT: "{ (Date) -> ... }" +// ELEMENT: "{ Date -> ... }" diff --git a/idea/testData/completion/smart/Lambda4.kt b/idea/testData/completion/smart/Lambda4.kt index 4079b76b003..57e9058c189 100644 --- a/idea/testData/completion/smart/Lambda4.kt +++ b/idea/testData/completion/smart/Lambda4.kt @@ -6,5 +6,5 @@ fun bar() { } // ABSENT: "{...}" -// EXIST: "{ (String) -> ... }" -// EXIST: "{ (Int) -> ... }" +// EXIST: "{ String -> ... }" +// EXIST: "{ Int -> ... }" diff --git a/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java index 6d0d47a2140..976765f96b8 100644 --- a/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/JSBasicCompletionTestGenerated.java @@ -189,6 +189,11 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes doTest("idea/testData/completion/basic/common/FunctionCompletionFormatting.kt"); } + @TestMetadata("HigherOrderFunction1.kt") + public void testHigherOrderFunction1() throws Exception { + doTest("idea/testData/completion/basic/common/HigherOrderFunction1.kt"); + } + @TestMetadata("InCallExpression.kt") public void testInCallExpression() throws Exception { doTest("idea/testData/completion/basic/common/InCallExpression.kt"); diff --git a/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java index e88eef2b8ee..7e00f03ed57 100644 --- a/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/JvmBasicCompletionTestGenerated.java @@ -16,17 +16,14 @@ package org.jetbrains.jet.completion; -import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestSuite; - -import java.io.File; -import java.util.regex.Pattern; import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.TestMetadata; -import org.jetbrains.jet.completion.AbstractJvmBasicCompletionTest; +import java.io.File; +import java.util.regex.Pattern; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @@ -189,6 +186,11 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest("idea/testData/completion/basic/common/FunctionCompletionFormatting.kt"); } + @TestMetadata("HigherOrderFunction1.kt") + public void testHigherOrderFunction1() throws Exception { + doTest("idea/testData/completion/basic/common/HigherOrderFunction1.kt"); + } + @TestMetadata("InCallExpression.kt") public void testInCallExpression() throws Exception { doTest("idea/testData/completion/basic/common/InCallExpression.kt"); diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/BasicCompletionHandlerTest.kt b/idea/tests/org/jetbrains/jet/completion/handlers/BasicCompletionHandlerTest.kt index 7f25c7a37fc..c9412ae6897 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/BasicCompletionHandlerTest.kt +++ b/idea/tests/org/jetbrains/jet/completion/handlers/BasicCompletionHandlerTest.kt @@ -46,6 +46,12 @@ public class BasicCompletionHandlerTest : CompletionHandlerTestBase(){ fun testHigherOrderFunctionWithArg() = doTest(2, "filterNot", null, '\n') + fun testHigherOrderFunctionWithArgs1() = doTest(1, "foo", "foo { (String, Char) -> ... }", null, '\n') + + fun testHigherOrderFunctionWithArgs2() = doTest(1, "foo", "foo(p: (String, Char) -> Boolean)", null, '\n') + + fun testHigherOrderFunctionWithArgs3() = doTest(1, "foo", "foo { (String, Char) -> ... }", null, '\n') + fun testForceParenthesisForTabChar() = doTest(0, "some", null, '\t') fun testTabInsertAtTheFileEnd() = doTest(0, "vvvvv", null, '\t') diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java index 70e855c00f5..6d60021175a 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java @@ -16,17 +16,11 @@ package org.jetbrains.jet.completion.handlers; -import junit.framework.Assert; -import junit.framework.Test; -import junit.framework.TestSuite; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.test.TestMetadata; import java.io.File; import java.util.regex.Pattern; -import org.jetbrains.jet.JetTestUtils; -import org.jetbrains.jet.test.InnerTestClasses; -import org.jetbrains.jet.test.TestMetadata; - -import org.jetbrains.jet.completion.handlers.AbstractSmartCompletionHandlerTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @@ -151,6 +145,16 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion doTest("idea/testData/completion/handlers/smart/ConstructorInsertsImport2.kt"); } + @TestMetadata("ConstructorWithLambdaParameter1.kt") + public void testConstructorWithLambdaParameter1() throws Exception { + doTest("idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter1.kt"); + } + + @TestMetadata("ConstructorWithLambdaParameter2.kt") + public void testConstructorWithLambdaParameter2() throws Exception { + doTest("idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter2.kt"); + } + @TestMetadata("ConstructorWithParameters.kt") public void testConstructorWithParameters() throws Exception { doTest("idea/testData/completion/handlers/smart/ConstructorWithParameters.kt");