"Add constructor parameters" fix - fixed display of too long signatures

This commit is contained in:
Valentin Kipyatkov
2015-05-13 22:56:39 +03:00
parent 04bee4dd7d
commit 0d8d030763
6 changed files with 109 additions and 36 deletions
@@ -38,11 +38,14 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.replaced
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import java.util.ArrayList
public object SuperClassNotInitialized : JetIntentionActionsFactory() {
private val DISPLAY_MAX_PARAMS = 5
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction>? {
val delegator = diagnostic.getPsiElement() as JetDelegatorToSuperClass
val classOrObjectDeclaration = delegator.getParent().getParent() as? JetClassOrObject ?: return null
@@ -65,10 +68,30 @@ public object SuperClassNotInitialized : JetIntentionActionsFactory() {
val typeArgsMap = superClass.getTypeConstructor().getParameters().zip(superType.getArguments()).toMap()
val substitutor = TypeUtils.makeSubstitutorForTypeParametersMap(typeArgsMap)
for (constructor in constructors) {
if (constructor.getValueParameters().isNotEmpty()) {
val substitutedConstructor = constructor.substitute(substitutor) as ConstructorDescriptor
fixes.add(AddParametersFix(delegator, classOrObjectDeclaration, substitutedConstructor))
val substitutedConstructors = constructors
.filter { it.getValueParameters().isNotEmpty() }
.map { it.substitute(substitutor) as ConstructorDescriptor }
if (substitutedConstructors.isNotEmpty()) {
val parameterTypes: List<List<JetType>> = substitutedConstructors.map {
it.getValueParameters().map { it.getType() }
}
fun canRenderOnlyFirstParameters(n: Int) = parameterTypes.map { it.take(n) }.toSet().size() == parameterTypes.size()
val maxParams = parameterTypes.map { it.size() }.max()!!
val maxParamsToDisplay = if (maxParams <= DISPLAY_MAX_PARAMS) {
maxParams
}
else {
(DISPLAY_MAX_PARAMS..maxParams-1).firstOrNull(::canRenderOnlyFirstParameters) ?: maxParams
}
for ((constructor, types) in substitutedConstructors.zip(parameterTypes)) {
val typesRendered = types.take(maxParamsToDisplay).map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it) }
val parameterString = typesRendered.joinToString(", ", "(", if (types.size() <= maxParamsToDisplay) ")" else ",...)")
val text = "Add constructor parameters from " + superClass.getName().asString() + parameterString
fixes.add(AddParametersFix(delegator, classOrObjectDeclaration, constructor, text))
}
}
}
@@ -103,58 +126,52 @@ public object SuperClassNotInitialized : JetIntentionActionsFactory() {
private class AddParametersFix(
element: JetDelegatorToSuperClass,
val classDeclaration: JetClass,
val superConstructor: ConstructorDescriptor
val superConstructor: ConstructorDescriptor, //TODO: do not hold descriptor!
private val text: String
) : JetIntentionAction<JetDelegatorToSuperClass>(element) {
override fun getFamilyName() = "Add constructor parameters from superclass"
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
//TODO: can ConstructorDescriptor become invalid?
return super.isAvailable(project, editor, file) && superConstructor.getValueParameters().none { it.getType().isError() }
}
override fun getText(): String {
// TODO: not too long
return "Add constructor parameters from " +
superConstructor.getContainingDeclaration().getName().asString() +
superConstructor.getValueParameters().map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it.getType()) }.joinToString(", ", "(", ")")
}
override fun getText()= text
override fun invoke(project: Project, editor: Editor?, file: JetFile) {
val factory = JetPsiFactory(project)
val renderer = IdeDescriptorRenderers.SOURCE_CODE
val superParameters = superConstructor.getValueParameters()
assert(superParameters.isNotEmpty())
val parameterNames = ArrayList<String>()
val typeRefsToShorten = ArrayList<JetTypeReference>()
if (!superParameters.isEmpty()) {
val parameterList = classDeclaration.createPrimaryConstructorParameterListIfAbsent()
val oldParameters = parameterList.getParameters()
val parametersToAdd = ArrayList<JetParameter>()
for (parameter in superParameters) {
val name = renderer.renderName(parameter.getName())
val varargElementType = parameter.getVarargElementType()
val parameterList = classDeclaration.createPrimaryConstructorParameterListIfAbsent()
val oldParameters = parameterList.getParameters()
val parametersToAdd = ArrayList<JetParameter>()
for (parameter in superParameters) {
val name = renderer.renderName(parameter.getName())
val varargElementType = parameter.getVarargElementType()
parameterNames.add(if (varargElementType != null) "*$name" else name)
parameterNames.add(if (varargElementType != null) "*$name" else name)
val nameString = parameter.getName().asString()
val existingParameter = oldParameters.firstOrNull { it.getName() == nameString }
if (existingParameter != null) {
val type = (existingParameter.resolveToDescriptor() as ValueParameterDescriptor).getType()
if (type.isSubtypeOf(parameter.getType())) continue // use existing parameter
}
val parameterText = if (varargElementType != null)
"vararg " + name + ":" + renderer.renderType(varargElementType)
else
name + ":" + renderer.renderType(parameter.getType())
parametersToAdd.add(factory.createParameter(parameterText))
val nameString = parameter.getName().asString()
val existingParameter = oldParameters.firstOrNull { it.getName() == nameString }
if (existingParameter != null) {
val type = (existingParameter.resolveToDescriptor() as ValueParameterDescriptor).getType()
if (type.isSubtypeOf(parameter.getType())) continue // use existing parameter
}
for (parameter in parametersToAdd) {
val newParameter = parameterList.addParameter(parameter)
typeRefsToShorten.add(newParameter.getTypeReference())
}
val parameterText = if (varargElementType != null)
"vararg " + name + ":" + renderer.renderType(varargElementType)
else
name + ":" + renderer.renderType(parameter.getType())
parametersToAdd.add(factory.createParameter(parameterText))
}
for (parameter in parametersToAdd) {
val newParameter = parameterList.addParameter(parameter)
typeRefsToShorten.add(newParameter.getTypeReference())
}
val delegatorCall = factory.createDelegatorToSuperCall(element.getText() + "(" + parameterNames.joinToString(",") + ")")
@@ -0,0 +1,11 @@
// "Add constructor parameters from Base(Int)" "true"
// ACTION: Add constructor parameters from Base(Char, Char, String, Int, Any?,...)
// ACTION: Add constructor parameters from Base(Char, Char, String?, Int, Any?)
open class Base {
constructor(p: Int){}
constructor(p1: Char, p2: Char, p3: String, p4: Int, p5: Any?, p6: String){}
constructor(p1: Char, p2: Char, p3: String?, p4: Int, p5: Any?){}
}
class C : Base<caret>
@@ -0,0 +1,11 @@
// "Add constructor parameters from Base(Int)" "true"
// ACTION: Add constructor parameters from Base(Char, Char, String, Int, Any?,...)
// ACTION: Add constructor parameters from Base(Char, Char, String?, Int, Any?)
open class Base {
constructor(p: Int){}
constructor(p1: Char, p2: Char, p3: String, p4: Int, p5: Any?, p6: String){}
constructor(p1: Char, p2: Char, p3: String?, p4: Int, p5: Any?){}
}
class C(p: Int) : Base(p)
@@ -0,0 +1,11 @@
// "Add constructor parameters from Base(Int)" "true"
// ACTION: Add constructor parameters from Base(Char, Char, String, Int, Any?, String, Boolean,...)
// ACTION: Add constructor parameters from Base(Char, Char, String, Int, Any?, String)
open class Base {
constructor(p: Int){}
constructor(p1: Char, p2: Char, p3: String, p4: Int, p5: Any?, p6: String, b: Boolean, c: Char){}
constructor(p1: Char, p2: Char, p3: String, p4: Int, _p5: Any?, p6: String){}
}
class C : Base<caret>
@@ -0,0 +1,11 @@
// "Add constructor parameters from Base(Int)" "true"
// ACTION: Add constructor parameters from Base(Char, Char, String, Int, Any?, String, Boolean,...)
// ACTION: Add constructor parameters from Base(Char, Char, String, Int, Any?, String)
open class Base {
constructor(p: Int){}
constructor(p1: Char, p2: Char, p3: String, p4: Int, p5: Any?, p6: String, b: Boolean, c: Char){}
constructor(p1: Char, p2: Char, p3: String, p4: Int, _p5: Any?, p6: String){}
}
class C(p: Int) : Base(p)
@@ -4896,6 +4896,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName);
}
@TestMetadata("displayLongSignatures1.kt")
public void testDisplayLongSignatures1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/supertypeInitialization/displayLongSignatures1.kt");
doTest(fileName);
}
@TestMetadata("displayLongSignatures2.kt")
public void testDisplayLongSignatures2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/supertypeInitialization/displayLongSignatures2.kt");
doTest(fileName);
}
@TestMetadata("genericClass.kt")
public void testGenericClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/supertypeInitialization/genericClass.kt");