"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.psi.psiUtil.replaced
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import java.util.ArrayList import java.util.ArrayList
public object SuperClassNotInitialized : JetIntentionActionsFactory() { public object SuperClassNotInitialized : JetIntentionActionsFactory() {
private val DISPLAY_MAX_PARAMS = 5
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction>? { override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction>? {
val delegator = diagnostic.getPsiElement() as JetDelegatorToSuperClass val delegator = diagnostic.getPsiElement() as JetDelegatorToSuperClass
val classOrObjectDeclaration = delegator.getParent().getParent() as? JetClassOrObject ?: return null 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 typeArgsMap = superClass.getTypeConstructor().getParameters().zip(superType.getArguments()).toMap()
val substitutor = TypeUtils.makeSubstitutorForTypeParametersMap(typeArgsMap) val substitutor = TypeUtils.makeSubstitutorForTypeParametersMap(typeArgsMap)
for (constructor in constructors) { val substitutedConstructors = constructors
if (constructor.getValueParameters().isNotEmpty()) { .filter { it.getValueParameters().isNotEmpty() }
val substitutedConstructor = constructor.substitute(substitutor) as ConstructorDescriptor .map { it.substitute(substitutor) as ConstructorDescriptor }
fixes.add(AddParametersFix(delegator, classOrObjectDeclaration, substitutedConstructor))
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( private class AddParametersFix(
element: JetDelegatorToSuperClass, element: JetDelegatorToSuperClass,
val classDeclaration: JetClass, val classDeclaration: JetClass,
val superConstructor: ConstructorDescriptor val superConstructor: ConstructorDescriptor, //TODO: do not hold descriptor!
private val text: String
) : JetIntentionAction<JetDelegatorToSuperClass>(element) { ) : JetIntentionAction<JetDelegatorToSuperClass>(element) {
override fun getFamilyName() = "Add constructor parameters from superclass" override fun getFamilyName() = "Add constructor parameters from superclass"
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { 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() } return super.isAvailable(project, editor, file) && superConstructor.getValueParameters().none { it.getType().isError() }
} }
override fun getText(): String { override fun getText()= text
// 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 invoke(project: Project, editor: Editor?, file: JetFile) { override fun invoke(project: Project, editor: Editor?, file: JetFile) {
val factory = JetPsiFactory(project) val factory = JetPsiFactory(project)
val renderer = IdeDescriptorRenderers.SOURCE_CODE val renderer = IdeDescriptorRenderers.SOURCE_CODE
val superParameters = superConstructor.getValueParameters() val superParameters = superConstructor.getValueParameters()
assert(superParameters.isNotEmpty())
val parameterNames = ArrayList<String>() val parameterNames = ArrayList<String>()
val typeRefsToShorten = ArrayList<JetTypeReference>() val typeRefsToShorten = ArrayList<JetTypeReference>()
if (!superParameters.isEmpty()) { val parameterList = classDeclaration.createPrimaryConstructorParameterListIfAbsent()
val parameterList = classDeclaration.createPrimaryConstructorParameterListIfAbsent() val oldParameters = parameterList.getParameters()
val oldParameters = parameterList.getParameters() val parametersToAdd = ArrayList<JetParameter>()
val parametersToAdd = ArrayList<JetParameter>() for (parameter in superParameters) {
for (parameter in superParameters) { val name = renderer.renderName(parameter.getName())
val name = renderer.renderName(parameter.getName()) val varargElementType = parameter.getVarargElementType()
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 nameString = parameter.getName().asString()
val existingParameter = oldParameters.firstOrNull { it.getName() == nameString } val existingParameter = oldParameters.firstOrNull { it.getName() == nameString }
if (existingParameter != null) { if (existingParameter != null) {
val type = (existingParameter.resolveToDescriptor() as ValueParameterDescriptor).getType() val type = (existingParameter.resolveToDescriptor() as ValueParameterDescriptor).getType()
if (type.isSubtypeOf(parameter.getType())) continue // use existing parameter 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))
} }
for (parameter in parametersToAdd) { val parameterText = if (varargElementType != null)
val newParameter = parameterList.addParameter(parameter) "vararg " + name + ":" + renderer.renderType(varargElementType)
typeRefsToShorten.add(newParameter.getTypeReference()) 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(",") + ")") 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); 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") @TestMetadata("genericClass.kt")
public void testGenericClass() throws Exception { public void testGenericClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/supertypeInitialization/genericClass.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/supertypeInitialization/genericClass.kt");