Some use of parameter names in completion

This commit is contained in:
Valentin Kipyatkov
2016-09-13 12:08:37 +03:00
parent 147d1da1ed
commit f335f32b48
6 changed files with 81 additions and 19 deletions
@@ -33,12 +33,17 @@ import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.fuzzyType import org.jetbrains.kotlin.idea.core.fuzzyType
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand 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.KtExpression
import org.jetbrains.kotlin.psi.KtFile 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.calls.util.getValueParametersCountFromFunctionType
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjection 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) { fun insertLambdaTemplate(context: InsertionContext, placeholderRange: TextRange, lambdaType: KotlinType) {
val explicitParameterTypes = needExplicitParameterTypes(context, placeholderRange, lambdaType) val explicitParameterTypes = needExplicitParameterTypes(context, placeholderRange, lambdaType)
@@ -53,11 +58,11 @@ fun insertLambdaTemplate(context: InsertionContext, placeholderRange: TextRange,
context.setLaterRunnable { context.setLaterRunnable {
context.project.executeWriteCommand(commandName, groupId = commandGroupId) { context.project.executeWriteCommand(commandName, groupId = commandGroupId) {
try { try {
if (rangeMarker.isValid()) { if (rangeMarker.isValid) {
context.getDocument().deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset()) context.document.deleteString(rangeMarker.startOffset, rangeMarker.endOffset)
context.getEditor().getCaretModel().moveToOffset(rangeMarker.getStartOffset()) context.editor.caretModel.moveToOffset(rangeMarker.startOffset)
val template = buildTemplate(lambdaType, explicitParameterTypes, context.getProject()) val template = buildTemplate(lambdaType, explicitParameterTypes, context.project)
TemplateManager.getInstance(context.getProject()).startTemplate(context.getEditor(), template) TemplateManager.getInstance(context.project).startTemplate(context.editor, template)
} }
} }
finally { finally {
@@ -69,8 +74,12 @@ fun insertLambdaTemplate(context: InsertionContext, placeholderRange: TextRange,
fun lambdaPresentation(lambdaType: KotlinType?): String { fun lambdaPresentation(lambdaType: KotlinType?): String {
if (lambdaType == null) return "{...}" if (lambdaType == null) return "{...}"
val parameterTypes = functionParameterTypes(lambdaType) val (parameterTypes, parameterNames) = functionParameterTypesAndNames(lambdaType)
val parametersPresentation = parameterTypes.map { IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(it) }.joinToString(", ") val parametersPresentation = parameterNames
.mapIndexed { index, name ->
name.check { !it.isSpecial } ?: IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(parameterTypes[index])
}
.joinToString(", ")
return "{ $parametersPresentation -> ... }" return "{ $parametersPresentation -> ... }"
} }
@@ -95,7 +104,7 @@ private fun needExplicitParameterTypes(context: InsertionContext, placeholderRan
} }
private fun buildTemplate(lambdaType: KotlinType, explicitParameterTypes: Boolean, project: Project): Template { private fun buildTemplate(lambdaType: KotlinType, explicitParameterTypes: Boolean, project: Project): Template {
val parameterTypes = functionParameterTypes(lambdaType) val (parameterTypes, parameterNames) = functionParameterTypesAndNames(lambdaType)
val manager = TemplateManager.getInstance(project) val manager = TemplateManager.getInstance(project)
@@ -109,7 +118,25 @@ private fun buildTemplate(lambdaType: KotlinType, explicitParameterTypes: Boolea
template.addTextSegment(", ") template.addTextSegment(", ")
} }
//TODO: check for names in scope //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<LookupElement>()
}
}
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) { if (explicitParameterTypes) {
template.addTextSegment(": " + IdeDescriptorRenderers.SOURCE_CODE.renderType(parameterType)) template.addTextSegment(": " + IdeDescriptorRenderers.SOURCE_CODE.renderType(parameterType))
} }
@@ -121,14 +148,9 @@ private fun buildTemplate(lambdaType: KotlinType, explicitParameterTypes: Boolea
return template return template
} }
private class ParameterNameExpression(val nameSuggestions: Array<String>) : Expression() { private fun functionParameterTypesAndNames(functionType: KotlinType): Pair<List<KotlinType>, List<Name>> {
override fun calculateResult(context: ExpressionContext?) = TextResult(nameSuggestions[0]) val types = getValueParameterTypesFromFunctionType(functionType).map(TypeProjection::getType)
val names = functionType.getFunctionTypeParameterNames() ?: types.map { SpecialNames.NO_NAME_PROVIDED }
override fun calculateQuickResult(context: ExpressionContext?): Result? = null assert(names.size == types.size)
return types to names
override fun calculateLookupItems(context: ExpressionContext?)
= Array<LookupElement>(nameSuggestions.size, { LookupElementBuilder.create(nameSuggestions[it]) })
} }
fun functionParameterTypes(functionType: KotlinType): List<KotlinType> =
getValueParameterTypesFromFunctionType(functionType).map(TypeProjection::getType)
@@ -0,0 +1,7 @@
fun foo(p: (word: String, number: Int) -> Unit){}
fun bar() {
foo(<caret>)
}
// ELEMENT: "{ word, number -> ... }"
@@ -0,0 +1,7 @@
fun foo(p: (word: String, number: Int) -> Unit){}
fun bar() {
foo({ word, number -> <caret> })
}
// ELEMENT: "{ word, number -> ... }"
@@ -0,0 +1,7 @@
fun foo(p: ((word: String, number: Int) -> Unit)?) {}
fun bar() {
foo(<caret>)
}
// ELEMENT: "{ word, number -> ... }"
@@ -0,0 +1,7 @@
fun foo(p: ((word: String, number: Int) -> Unit)?) {}
fun bar() {
foo({ word, number -> <caret> })
}
// ELEMENT: "{ word, number -> ... }"
@@ -906,5 +906,17 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/lambda/InsertImport.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/smart/lambda/InsertImport.kt");
doTest(fileName); 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);
}
} }
} }