From f335f32b486ef852e618e92f556fe99ebdf65ba5 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 13 Sep 2016 12:08:37 +0300 Subject: [PATCH] Some use of parameter names in completion --- .../completion/handlers/GenerateLambda.kt | 60 +++++++++++++------ .../smart/lambda/ParameterNamesSpecified.kt | 7 +++ .../lambda/ParameterNamesSpecified.kt.after | 7 +++ .../ParameterNamesSpecified_NullableType.kt | 7 +++ ...ameterNamesSpecified_NullableType.kt.after | 7 +++ .../SmartCompletionHandlerTestGenerated.java | 12 ++++ 6 files changed, 81 insertions(+), 19 deletions(-) create mode 100644 idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified.kt create mode 100644 idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified.kt.after create mode 100644 idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified_NullableType.kt create mode 100644 idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified_NullableType.kt.after diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt index 766fed072b6..62726853ce4 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt @@ -33,12 +33,17 @@ import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.core.fuzzyType import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.calls.util.getValueParametersCountFromFunctionType import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeProjection +import org.jetbrains.kotlin.types.getFunctionTypeParameterNames +import org.jetbrains.kotlin.utils.addToStdlib.check fun insertLambdaTemplate(context: InsertionContext, placeholderRange: TextRange, lambdaType: KotlinType) { val explicitParameterTypes = needExplicitParameterTypes(context, placeholderRange, lambdaType) @@ -53,11 +58,11 @@ fun insertLambdaTemplate(context: InsertionContext, placeholderRange: TextRange, context.setLaterRunnable { context.project.executeWriteCommand(commandName, groupId = commandGroupId) { 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) + if (rangeMarker.isValid) { + context.document.deleteString(rangeMarker.startOffset, rangeMarker.endOffset) + context.editor.caretModel.moveToOffset(rangeMarker.startOffset) + val template = buildTemplate(lambdaType, explicitParameterTypes, context.project) + TemplateManager.getInstance(context.project).startTemplate(context.editor, template) } } finally { @@ -69,8 +74,12 @@ fun insertLambdaTemplate(context: InsertionContext, placeholderRange: TextRange, fun lambdaPresentation(lambdaType: KotlinType?): String { if (lambdaType == null) return "{...}" - val parameterTypes = functionParameterTypes(lambdaType) - val parametersPresentation = parameterTypes.map { IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(it) }.joinToString(", ") + val (parameterTypes, parameterNames) = functionParameterTypesAndNames(lambdaType) + val parametersPresentation = parameterNames + .mapIndexed { index, name -> + name.check { !it.isSpecial } ?: IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(parameterTypes[index]) + } + .joinToString(", ") return "{ $parametersPresentation -> ... }" } @@ -95,7 +104,7 @@ private fun needExplicitParameterTypes(context: InsertionContext, placeholderRan } private fun buildTemplate(lambdaType: KotlinType, explicitParameterTypes: Boolean, project: Project): Template { - val parameterTypes = functionParameterTypes(lambdaType) + val (parameterTypes, parameterNames) = functionParameterTypesAndNames(lambdaType) val manager = TemplateManager.getInstance(project) @@ -109,7 +118,25 @@ private fun buildTemplate(lambdaType: KotlinType, explicitParameterTypes: Boolea template.addTextSegment(", ") } //TODO: check for names in scope - template.addVariable(ParameterNameExpression(KotlinNameSuggester.suggestNamesByType(parameterType, { true }, "p").toTypedArray()), true) + val parameterName = parameterNames[i].check { !it.isSpecial }?.render() + val nameExpression = if (parameterName != null) { + object : Expression() { + override fun calculateResult(context: ExpressionContext?) = TextResult(parameterName) + override fun calculateQuickResult(context: ExpressionContext?): Result? = TextResult(parameterName) + override fun calculateLookupItems(context: ExpressionContext?) = emptyArray() + } + } + else { + val nameSuggestions = KotlinNameSuggester.suggestNamesByType(parameterType, { true }, "p").toTypedArray() + object : Expression() { + override fun calculateResult(context: ExpressionContext?) = TextResult(nameSuggestions[0]) + override fun calculateQuickResult(context: ExpressionContext?): Result? = null + override fun calculateLookupItems(context: ExpressionContext?) + = nameSuggestions.map { LookupElementBuilder.create(it) }.toTypedArray() + } + } + template.addVariable(nameExpression, true) + if (explicitParameterTypes) { template.addTextSegment(": " + IdeDescriptorRenderers.SOURCE_CODE.renderType(parameterType)) } @@ -121,14 +148,9 @@ private fun buildTemplate(lambdaType: KotlinType, explicitParameterTypes: Boolea 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]) }) +private fun functionParameterTypesAndNames(functionType: KotlinType): Pair, List> { + val types = getValueParameterTypesFromFunctionType(functionType).map(TypeProjection::getType) + val names = functionType.getFunctionTypeParameterNames() ?: types.map { SpecialNames.NO_NAME_PROVIDED } + assert(names.size == types.size) + return types to names } - -fun functionParameterTypes(functionType: KotlinType): List = - getValueParameterTypesFromFunctionType(functionType).map(TypeProjection::getType) diff --git a/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified.kt b/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified.kt new file mode 100644 index 00000000000..0e36126cb91 --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified.kt @@ -0,0 +1,7 @@ +fun foo(p: (word: String, number: Int) -> Unit){} + +fun bar() { + foo() +} + +// ELEMENT: "{ word, number -> ... }" diff --git a/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified.kt.after b/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified.kt.after new file mode 100644 index 00000000000..e30d6f87386 --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified.kt.after @@ -0,0 +1,7 @@ +fun foo(p: (word: String, number: Int) -> Unit){} + +fun bar() { + foo({ word, number -> }) +} + +// ELEMENT: "{ word, number -> ... }" diff --git a/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified_NullableType.kt b/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified_NullableType.kt new file mode 100644 index 00000000000..a5b312ab56a --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified_NullableType.kt @@ -0,0 +1,7 @@ +fun foo(p: ((word: String, number: Int) -> Unit)?) {} + +fun bar() { + foo() +} + +// ELEMENT: "{ word, number -> ... }" diff --git a/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified_NullableType.kt.after b/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified_NullableType.kt.after new file mode 100644 index 00000000000..cfab81c3e93 --- /dev/null +++ b/idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified_NullableType.kt.after @@ -0,0 +1,7 @@ +fun foo(p: ((word: String, number: Int) -> Unit)?) {} + +fun bar() { + foo({ word, number -> }) +} + +// ELEMENT: "{ word, number -> ... }" diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java index a9b0a35381a..56610b25c0c 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/SmartCompletionHandlerTestGenerated.java @@ -906,5 +906,17 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/lambda/InsertImport.kt"); doTest(fileName); } + + @TestMetadata("ParameterNamesSpecified.kt") + public void testParameterNamesSpecified() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified.kt"); + doTest(fileName); + } + + @TestMetadata("ParameterNamesSpecified_NullableType.kt") + public void testParameterNamesSpecified_NullableType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/lambda/ParameterNamesSpecified_NullableType.kt"); + doTest(fileName); + } } }