Some use of parameter names in completion
This commit is contained in:
+41
-19
@@ -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)
|
|
||||||
|
|||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo(p: (word: String, number: Int) -> Unit){}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo(<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELEMENT: "{ word, number -> ... }"
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo(p: (word: String, number: Int) -> Unit){}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo({ word, number -> <caret> })
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELEMENT: "{ word, number -> ... }"
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo(p: ((word: String, number: Int) -> Unit)?) {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo(<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELEMENT: "{ word, number -> ... }"
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo(p: ((word: String, number: Int) -> Unit)?) {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo({ word, number -> <caret> })
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELEMENT: "{ word, number -> ... }"
|
||||||
+12
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user