Create From Usage: Generalize function builder for both functions and properties

This commit is contained in:
Alexey Sedunov
2014-09-29 15:02:14 +04:00
parent 9032a6166e
commit be211d7cda
15 changed files with 130 additions and 87 deletions
@@ -216,9 +216,8 @@ public class JetPsiFactory(private val project: Project) {
return createClass("class A(){}").getBody()!! return createClass("class A(){}").getBody()!!
} }
public fun createParameter(name: String, `type`: String): JetParameter { public fun createParameter(text : String): JetParameter {
val function = createFunction("fun foo(" + name + " : " + `type` + ") {}") return createClass("class A($text)").getPrimaryConstructorParameters().first()
return function.getValueParameters().first()
} }
public fun createParameterList(text: String): JetParameterList { public fun createParameterList(text: String): JetParameterList {
@@ -90,18 +90,18 @@ class TypeCandidate(val theType: JetType, scope: JetScope? = null) {
} }
} }
class FunctionBuilderConfiguration( class CallableBuilderConfiguration(
val functionInfo: FunctionInfo, val callableInfo: CallableInfo,
val currentFile: JetFile, val currentFile: JetFile,
val currentEditor: Editor val currentEditor: Editor
) )
trait FunctionPlacement { trait CallablePlacement {
class WithReceiver(val receiverTypeCandidate: TypeCandidate): FunctionPlacement class WithReceiver(val receiverTypeCandidate: TypeCandidate): CallablePlacement
class NoReceiver(val containingElement: JetElement): FunctionPlacement class NoReceiver(val containingElement: JetElement): CallablePlacement
} }
class FunctionBuilder(val config: FunctionBuilderConfiguration) { class CallableBuilder(val config: CallableBuilderConfiguration) {
private var finished: Boolean = false private var finished: Boolean = false
val currentFileContext: BindingContext val currentFileContext: BindingContext
@@ -115,7 +115,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
currentFileModule = exhaust.getModuleDescriptor() currentFileModule = exhaust.getModuleDescriptor()
} }
public var placement: FunctionPlacement by Delegates.notNull() public var placement: CallablePlacement by Delegates.notNull()
fun computeTypeCandidates(typeInfo: TypeInfo): List<TypeCandidate> = fun computeTypeCandidates(typeInfo: TypeInfo): List<TypeCandidate> =
typeCandidates.getOrPut(typeInfo) { typeInfo.getPossibleTypes(this).map { TypeCandidate(it) } } typeCandidates.getOrPut(typeInfo) { typeInfo.getPossibleTypes(this).map { TypeCandidate(it) } }
@@ -174,18 +174,18 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
val placement = placement val placement = placement
when { when {
placement is FunctionPlacement.NoReceiver -> { placement is CallablePlacement.NoReceiver -> {
receiverClassDescriptor = null receiverClassDescriptor = null
isExtension = false isExtension = false
containingElement = placement.containingElement containingElement = placement.containingElement
} }
placement is FunctionPlacement.WithReceiver -> { placement is CallablePlacement.WithReceiver -> {
receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(placement.receiverTypeCandidate.theType) receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(placement.receiverTypeCandidate.theType)
val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.classDescriptorToDeclaration(it) } val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.classDescriptorToDeclaration(it) }
isExtension = !(classDeclaration is JetClassOrObject && classDeclaration.isWritable()) isExtension = !(classDeclaration is JetClassOrObject && classDeclaration.isWritable())
containingElement = if (isExtension) config.currentFile else classDeclaration as JetElement containingElement = if (isExtension) config.currentFile else classDeclaration as JetElement
} }
else -> throw IllegalArgumentException("Unexpected function kind: $placement") else -> throw IllegalArgumentException("Unexpected placement: $placement")
} }
val receiverType = receiverClassDescriptor?.getDefaultType() val receiverType = receiverClassDescriptor?.getDefaultType()
@@ -198,7 +198,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
containingFileEditor = config.currentEditor containingFileEditor = config.currentEditor
} }
isUnit = config.functionInfo.returnTypeInfo.let { it is TypeInfo.ByType && it.theType.isUnit() } isUnit = config.callableInfo.returnTypeInfo.let { it is TypeInfo.ByType && it.theType.isUnit() }
val scope = if (isExtension || receiverClassDescriptor == null) { val scope = if (isExtension || receiverClassDescriptor == null) {
currentFileModule.getPackage(config.currentFile.getPackageFqName())!!.getMemberScope() currentFileModule.getPackage(config.currentFile.getPackageFqName())!!.getMemberScope()
@@ -209,15 +209,15 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
// figure out type substitutions for type parameters // figure out type substitutions for type parameters
val classTypeParameters = receiverType?.getArguments() ?: Collections.emptyList() val classTypeParameters = receiverType?.getArguments() ?: Collections.emptyList()
val ownerTypeArguments = (placement as? FunctionPlacement.WithReceiver)?.receiverTypeCandidate?.theType?.getArguments() val ownerTypeArguments = (placement as? CallablePlacement.WithReceiver)?.receiverTypeCandidate?.theType?.getArguments()
?: Collections.emptyList() ?: Collections.emptyList()
assert(ownerTypeArguments.size == classTypeParameters.size) assert(ownerTypeArguments.size == classTypeParameters.size)
val substitutions = ownerTypeArguments.zip(classTypeParameters).map { val substitutions = ownerTypeArguments.zip(classTypeParameters).map {
JetTypeSubstitution(it.first.getType(), it.second.getType()) JetTypeSubstitution(it.first.getType(), it.second.getType())
}.copyToArray() }.copyToArray()
config.functionInfo.parameterInfos.forEach { parameter -> computeTypeCandidates(parameter.typeInfo, substitutions, scope) } config.callableInfo.parameterInfos.forEach { parameter -> computeTypeCandidates(parameter.typeInfo, substitutions, scope) }
if (!isUnit) { if (!isUnit) {
computeTypeCandidates(config.functionInfo.returnTypeInfo, substitutions, scope) computeTypeCandidates(config.callableInfo.returnTypeInfo, substitutions, scope)
} }
// now that we have done substitutions, we can throw it away // now that we have done substitutions, we can throw it away
@@ -225,9 +225,9 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
// figure out type parameter renames to avoid conflicts // figure out type parameter renames to avoid conflicts
typeParameterNameMap = getTypeParameterRenames(scope) typeParameterNameMap = getTypeParameterRenames(scope)
config.functionInfo.parameterInfos.forEach { renderTypeCandidates(it.typeInfo, typeParameterNameMap) } config.callableInfo.parameterInfos.forEach { renderTypeCandidates(it.typeInfo, typeParameterNameMap) }
if (!isUnit) { if (!isUnit) {
renderTypeCandidates(config.functionInfo.returnTypeInfo, typeParameterNameMap) renderTypeCandidates(config.callableInfo.returnTypeInfo, typeParameterNameMap)
} }
receiverTypeCandidate?.render(typeParameterNameMap) receiverTypeCandidate?.render(typeParameterNameMap)
} }
@@ -239,13 +239,22 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap) } typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap) }
} }
private fun createFunctionSkeleton(): JetNamedFunction { private fun createDeclarationSkeleton(): JetCallableDeclaration {
with (config) { with (config) {
val parametersString = functionInfo.parameterInfos.indices.map { i -> "p$i: Any" }.joinToString(", ")
val returnTypeString = if (isUnit) "" else ": Any"
val ownerTypeString = if (isExtension) "${receiverTypeCandidate!!.renderedType!!}." else "" val ownerTypeString = if (isExtension) "${receiverTypeCandidate!!.renderedType!!}." else ""
val paramList = when (callableInfo.kind) {
CallableKind.FUNCTION -> "(${callableInfo.parameterInfos.indices.map { i -> "p$i: Any" }.joinToString(", ")})"
CallableKind.PROPERTY -> ""
}
val returnTypeString = if (isUnit) "" else ": Any"
val header = "$ownerTypeString${callableInfo.name}$paramList$returnTypeString"
val psiFactory = JetPsiFactory(currentFile) val psiFactory = JetPsiFactory(currentFile)
val func = psiFactory.createFunction("fun $ownerTypeString${functionInfo.name}($parametersString)$returnTypeString { }")
val declaration = when (callableInfo.kind) {
CallableKind.FUNCTION -> psiFactory.createFunction("fun $header {}")
CallableKind.PROPERTY -> psiFactory.createProperty("val $header")
}
val newLine = psiFactory.createNewLine() val newLine = psiFactory.createNewLine()
fun prepend(element: PsiElement, elementBeforeStart: PsiElement): PsiElement { fun prepend(element: PsiElement, elementBeforeStart: PsiElement): PsiElement {
@@ -275,7 +284,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
} }
when (containingElement) { when (containingElement) {
is JetFile -> return append(func, containingElement.getLastChild()!!, false) as JetNamedFunction is JetFile -> return append(declaration, containingElement.getLastChild()!!, false) as JetCallableDeclaration
is JetClassOrObject -> { is JetClassOrObject -> {
var classBody = containingElement.getBody() var classBody = containingElement.getBody()
@@ -284,11 +293,11 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
containingElement.addBefore(psiFactory.createWhiteSpace(), classBody) containingElement.addBefore(psiFactory.createWhiteSpace(), classBody)
} }
val rBrace = classBody!!.getRBrace() val rBrace = classBody!!.getRBrace()
return (rBrace?.let { append(func, it, true) } return (rBrace?.let { append(declaration, it, true) }
?: append(func, classBody!!.getLastChild()!!, false)) as JetNamedFunction ?: append(declaration, classBody!!.getLastChild()!!, false)) as JetCallableDeclaration
} }
is JetBlockExpression -> return prepend(func, containingElement.getLBrace()!!) as JetNamedFunction is JetBlockExpression -> return prepend(declaration, containingElement.getLBrace()!!) as JetCallableDeclaration
else -> throw AssertionError("Invalid containing element: ${containingElement.getText()}") else -> throw AssertionError("Invalid containing element: ${containingElement.getText()}")
} }
@@ -300,13 +309,13 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
allTypeParametersNotInScope.addAll(receiverTypeCandidate?.typeParameters?.toList() ?: Collections.emptyList()) allTypeParametersNotInScope.addAll(receiverTypeCandidate?.typeParameters?.toList() ?: Collections.emptyList())
config.functionInfo.parameterInfos.stream() config.callableInfo.parameterInfos.stream()
.flatMap { typeCandidates[it.typeInfo]!!.stream() } .flatMap { typeCandidates[it.typeInfo]!!.stream() }
.flatMap { it.typeParameters.stream() } .flatMap { it.typeParameters.stream() }
.toCollection(allTypeParametersNotInScope) .toCollection(allTypeParametersNotInScope)
if (!isUnit) { if (!isUnit) {
computeTypeCandidates(config.functionInfo.returnTypeInfo).stream().flatMapTo(allTypeParametersNotInScope) { it.typeParameters.stream() } computeTypeCandidates(config.callableInfo.returnTypeInfo).stream().flatMapTo(allTypeParametersNotInScope) { it.typeParameters.stream() }
} }
val validator = CollectingValidator { scope.getClassifier(Name.identifier(it)) == null } val validator = CollectingValidator { scope.getClassifier(Name.identifier(it)) == null }
@@ -316,14 +325,14 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
} }
private fun setupTypeReferencesForShortening( private fun setupTypeReferencesForShortening(
func: JetNamedFunction, declaration: JetCallableDeclaration,
typeRefsToShorten: MutableList<JetTypeReference>, parameterTypeExpressions: List<TypeExpression>, typeRefsToShorten: MutableList<JetTypeReference>, parameterTypeExpressions: List<TypeExpression>,
returnTypeExpression: TypeExpression?) { returnTypeExpression: TypeExpression?) {
if (isExtension) { if (isExtension) {
val receiverTypeRef = JetPsiFactory(func).createType(receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap)) val receiverTypeRef = JetPsiFactory(declaration).createType(receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap))
replaceWithLongerName(receiverTypeRef, receiverTypeCandidate.theType) replaceWithLongerName(receiverTypeRef, receiverTypeCandidate.theType)
val funcReceiverTypeRef = func.getReceiverTypeRef() val funcReceiverTypeRef = declaration.getReceiverTypeRef()
if (funcReceiverTypeRef != null) { if (funcReceiverTypeRef != null) {
typeRefsToShorten.add(funcReceiverTypeRef) typeRefsToShorten.add(funcReceiverTypeRef)
} }
@@ -331,24 +340,30 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
if (!isUnit) { if (!isUnit) {
returnTypeExpression!! returnTypeExpression!!
val returnTypeRef = func.getReturnTypeRef() val returnTypeRef = declaration.getReturnTypeRef()
if (returnTypeRef != null) { if (returnTypeRef != null) {
val returnType = returnTypeExpression.getTypeFromSelection(returnTypeRef.getText() ?: throw AssertionError("Expression for function return type shouldn't be empty: function = ${func.getText()}")) val returnType = returnTypeExpression.getTypeFromSelection(
returnTypeRef.getText()
?: throw AssertionError("Expression for return type shouldn't be empty: declaration = ${declaration.getText()}")
)
if (returnType != null) { if (returnType != null) {
// user selected a given type // user selected a given type
replaceWithLongerName(returnTypeRef, returnType) replaceWithLongerName(returnTypeRef, returnType)
typeRefsToShorten.add(func.getReturnTypeRef()!!) typeRefsToShorten.add(declaration.getReturnTypeRef()!!)
} }
} }
} }
val valueParameters = func.getValueParameters() val valueParameters = declaration.getValueParameterList()?.getParameters() ?: Collections.emptyList<JetParameter>()
val parameterIndicesToShorten = ArrayList<Int>() val parameterIndicesToShorten = ArrayList<Int>()
assert(valueParameters.size == parameterTypeExpressions.size) assert(valueParameters.size == parameterTypeExpressions.size)
for ((i, parameter) in valueParameters.stream().withIndices()) { for ((i, parameter) in valueParameters.stream().withIndices()) {
val parameterTypeRef = parameter.getTypeReference() val parameterTypeRef = parameter.getTypeReference()
if (parameterTypeRef != null) { if (parameterTypeRef != null) {
val parameterType = parameterTypeExpressions[i].getTypeFromSelection(parameterTypeRef.getText() ?: throw AssertionError("Expression for function parameter type shouldn't be empty: function = ${func.getText()}")) val parameterType = parameterTypeExpressions[i].getTypeFromSelection(
parameterTypeRef.getText()
?: throw AssertionError("Expression for parameter type shouldn't be empty: declaration = ${declaration.getText()}")
)
if (parameterType != null) { if (parameterType != null) {
replaceWithLongerName(parameterTypeRef, parameterType) replaceWithLongerName(parameterTypeRef, parameterType)
parameterIndicesToShorten.add(i) parameterIndicesToShorten.add(i)
@@ -356,8 +371,11 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
} }
} }
val expandedValueParameters = func.getValueParameters() declaration.getValueParameterList()?.getParameters()?.let { expandedValueParameters ->
parameterIndicesToShorten.stream().map { expandedValueParameters[it].getTypeReference() }.filterNotNullTo(typeRefsToShorten) parameterIndicesToShorten.stream()
.map { expandedValueParameters[it].getTypeReference() }
.filterNotNullTo(typeRefsToShorten)
}
} }
private fun setupFunctionBody(func: JetNamedFunction) { private fun setupFunctionBody(func: JetNamedFunction) {
@@ -368,7 +386,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFqName(it).asString()) properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFqName(it).asString())
properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, it.getName().asString()) properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, it.getName().asString())
} }
properties.setProperty(ATTRIBUTE_FUNCTION_NAME, config.functionInfo.name) properties.setProperty(ATTRIBUTE_FUNCTION_NAME, config.callableInfo.name)
val bodyText = try { val bodyText = try {
fileTemplate!!.getText(properties) fileTemplate!!.getText(properties)
@@ -386,37 +404,36 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
func.getBodyExpression()!!.replace(newBodyExpression) func.getBodyExpression()!!.replace(newBodyExpression)
} }
private fun setupReturnTypeTemplate(builder: TemplateBuilder, func: JetNamedFunction): TypeExpression { private fun setupReturnTypeTemplate(builder: TemplateBuilder, declaration: JetCallableDeclaration): TypeExpression {
val returnTypeRef = func.getReturnTypeRef()!! val returnTypeRef = declaration.getReturnTypeRef()!!
val returnTypeExpression = TypeExpression(typeCandidates[config.functionInfo.returnTypeInfo]!!) val returnTypeExpression = TypeExpression(typeCandidates[config.callableInfo.returnTypeInfo]!!)
builder.replaceElement(returnTypeRef, returnTypeExpression) builder.replaceElement(returnTypeRef, returnTypeExpression)
return returnTypeExpression return returnTypeExpression
} }
private fun setupTypeParameterListTemplate(builder: TemplateBuilderImpl, func: JetNamedFunction): TypeParameterListExpression { private fun setupTypeParameterListTemplate(builder: TemplateBuilderImpl, declaration: JetCallableDeclaration): TypeParameterListExpression {
val typeParameterMap = HashMap<String, Array<String>>() val typeParameterMap = HashMap<String, Array<String>>()
val receiverTypeParameterNames = receiverTypeCandidate?.let { it.typeParameterNames!! } ?: ArrayUtil.EMPTY_STRING_ARRAY val receiverTypeParameterNames = receiverTypeCandidate?.let { it.typeParameterNames!! } ?: ArrayUtil.EMPTY_STRING_ARRAY
config.functionInfo.parameterInfos.stream().flatMap { typeCandidates[it.typeInfo]!!.stream() }.forEach { config.callableInfo.parameterInfos.stream().flatMap { typeCandidates[it.typeInfo]!!.stream() }.forEach {
typeParameterMap[it.renderedType!!] = it.typeParameterNames!! typeParameterMap[it.renderedType!!] = it.typeParameterNames!!
} }
if (func.getReturnTypeRef() != null) { if (declaration.getReturnTypeRef() != null) {
typeCandidates[config.functionInfo.returnTypeInfo]!!.forEach { typeCandidates[config.callableInfo.returnTypeInfo]!!.forEach {
typeParameterMap[it.renderedType!!] = it.typeParameterNames!! typeParameterMap[it.renderedType!!] = it.typeParameterNames!!
} }
} }
// ((3, 3) is after "fun") // ((3, 3) is after "fun")
builder.replaceElement(func, TextRange.create(3, 3), TYPE_PARAMETER_LIST_VARIABLE_NAME, null, false) builder.replaceElement(declaration, TextRange.create(3, 3), TYPE_PARAMETER_LIST_VARIABLE_NAME, null, false)
return TypeParameterListExpression(receiverTypeParameterNames, typeParameterMap) return TypeParameterListExpression(receiverTypeParameterNames, typeParameterMap)
} }
private fun setupParameterTypeTemplates(builder: TemplateBuilder, parameterList: JetParameterList): List<TypeExpression> { private fun setupParameterTypeTemplates(builder: TemplateBuilder, parameterList: List<JetParameter>): List<TypeExpression> {
val jetParameters = parameterList.getParameters() assert(parameterList.size == config.callableInfo.parameterInfos.size)
assert(jetParameters.size == config.functionInfo.parameterInfos.size)
val typeParameters = ArrayList<TypeExpression>() val typeParameters = ArrayList<TypeExpression>()
for ((parameter, jetParameter) in config.functionInfo.parameterInfos.zip(jetParameters)) { for ((parameter, jetParameter) in config.callableInfo.parameterInfos.zip(parameterList)) {
val parameterTypeExpression = TypeExpression(typeCandidates[parameter.typeInfo]!!) val parameterTypeExpression = TypeExpression(typeCandidates[parameter.typeInfo]!!)
val parameterTypeRef = jetParameter.getTypeReference()!! val parameterTypeRef = jetParameter.getTypeReference()!!
builder.replaceElement(parameterTypeRef, parameterTypeExpression) builder.replaceElement(parameterTypeRef, parameterTypeExpression)
@@ -454,9 +471,8 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
} }
fun buildAndRunTemplate() { fun buildAndRunTemplate() {
val func = createFunctionSkeleton() val declaration = createDeclarationSkeleton()
val project = func.getProject() val project = declaration.getProject()
val parameterList = func.getValueParameterList()!!
// build templates // build templates
PsiDocumentManager.getInstance(project).commitAllDocuments() PsiDocumentManager.getInstance(project).commitAllDocuments()
@@ -466,20 +482,21 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
caretModel.moveToOffset(containingFile.getNode().getStartOffset()) caretModel.moveToOffset(containingFile.getNode().getStartOffset())
val builder = TemplateBuilderImpl(containingFile) val builder = TemplateBuilderImpl(containingFile)
val returnTypeExpression = if (isUnit) null else setupReturnTypeTemplate(builder, func) val returnTypeExpression = if (isUnit) null else setupReturnTypeTemplate(builder, declaration)
val parameterTypeExpressions = setupParameterTypeTemplates(builder, parameterList) val parameterTypeExpressions =
setupParameterTypeTemplates(builder, declaration.getValueParameterList()?.getParameters() ?: Collections.emptyList())
// add a segment for the parameter list // add a segment for the parameter list
// Note: because TemplateBuilderImpl does not have a replaceElement overload that takes in both a TextRange and alwaysStopAt, we // Note: because TemplateBuilderImpl does not have a replaceElement overload that takes in both a TextRange and alwaysStopAt, we
// need to create the segment first and then hack the Expression into the template later. We use this template to update the type // need to create the segment first and then hack the Expression into the template later. We use this template to update the type
// parameter list as the user makes selections in the parameter types, and we need alwaysStopAt to be false so the user can't tab to // parameter list as the user makes selections in the parameter types, and we need alwaysStopAt to be false so the user can't tab to
// it. // it.
val expression = setupTypeParameterListTemplate(builder, func) val expression = setupTypeParameterListTemplate(builder, declaration)
// the template built by TemplateBuilderImpl is ordered by element position, but we want types to be first, so hack it // the template built by TemplateBuilderImpl is ordered by element position, but we want types to be first, so hack it
val templateImpl = builder.buildInlineTemplate() as TemplateImpl val templateImpl = builder.buildInlineTemplate() as TemplateImpl
val variables = templateImpl.getVariables()!! val variables = templateImpl.getVariables()!!
for (i in 0..(config.functionInfo.parameterInfos.size - 1)) { for (i in 0..(config.callableInfo.parameterInfos.size - 1)) {
Collections.swap(variables, i * 2, i * 2 + 1) Collections.swap(variables, i * 2, i * 2 + 1)
} }
@@ -494,15 +511,19 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
override fun templateFinished(template: Template?, brokenOff: Boolean) { override fun templateFinished(template: Template?, brokenOff: Boolean) {
// file templates // file templates
val offset = templateImpl.getSegmentOffset(0) val offset = templateImpl.getSegmentOffset(0)
val newFunc = PsiTreeUtil.findElementOfClassAtOffset(containingFile, offset, javaClass<JetNamedFunction>(), false)!! val newDeclaration = PsiTreeUtil.findElementOfClassAtOffset(
containingFile, offset, javaClass<JetCallableDeclaration>(), false
)!!
val typeRefsToShorten = ArrayList<JetTypeReference>() val typeRefsToShorten = ArrayList<JetTypeReference>()
ApplicationManager.getApplication()!!.runWriteAction { ApplicationManager.getApplication()!!.runWriteAction {
// file templates // file templates
setupFunctionBody(newFunc) if (newDeclaration is JetNamedFunction) {
setupFunctionBody(newDeclaration)
}
// change short type names to fully qualified ones (to be shortened below) // change short type names to fully qualified ones (to be shortened below)
setupTypeReferencesForShortening(newFunc, typeRefsToShorten, parameterTypeExpressions, returnTypeExpression) setupTypeReferencesForShortening(newDeclaration, typeRefsToShorten, parameterTypeExpressions, returnTypeExpression)
ShortenReferences.process(typeRefsToShorten) ShortenReferences.process(typeRefsToShorten)
} }
} }
@@ -512,4 +533,4 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) {
} }
} }
fun FunctionBuilderConfiguration.createBuilder(): FunctionBuilder = FunctionBuilder(this) fun CallableBuilderConfiguration.createBuilder(): CallableBuilder = CallableBuilder(this)
@@ -16,7 +16,7 @@ import org.jetbrains.jet.plugin.util.supertypes
*/ */
abstract class TypeInfo(val variance: Variance) { abstract class TypeInfo(val variance: Variance) {
object Empty: TypeInfo(Variance.INVARIANT) { object Empty: TypeInfo(Variance.INVARIANT) {
override fun getPossibleTypes(builder: FunctionBuilder): List<JetType> = Collections.emptyList() override fun getPossibleTypes(builder: CallableBuilder): List<JetType> = Collections.emptyList()
} }
class ByExpression(val expression: JetExpression, variance: Variance): TypeInfo(variance) { class ByExpression(val expression: JetExpression, variance: Variance): TypeInfo(variance) {
@@ -24,22 +24,22 @@ abstract class TypeInfo(val variance: Variance) {
JetNameSuggester.suggestNamesForExpression(expression, EmptyValidator) JetNameSuggester.suggestNamesForExpression(expression, EmptyValidator)
} }
override fun getPossibleTypes(builder: FunctionBuilder): List<JetType> = override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
expression.guessTypes(builder.currentFileContext).flatMap { it.getPossibleSupertypes(variance) } expression.guessTypes(builder.currentFileContext).flatMap { it.getPossibleSupertypes(variance) }
} }
class ByType(val theType: JetType, variance: Variance, val keepUnsubstituted: Boolean = false): TypeInfo(variance) { class ByType(val theType: JetType, variance: Variance, val keepUnsubstituted: Boolean = false): TypeInfo(variance) {
override fun getPossibleTypes(builder: FunctionBuilder): List<JetType> = override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
theType.getPossibleSupertypes(variance) theType.getPossibleSupertypes(variance)
} }
class ByReceiverType(variance: Variance): TypeInfo(variance) { class ByReceiverType(variance: Variance): TypeInfo(variance) {
override fun getPossibleTypes(builder: FunctionBuilder): List<JetType> = override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
(builder.placement as FunctionPlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance) (builder.placement as CallablePlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance)
} }
open val possibleNamesFromExpression: Array<String> get() = ArrayUtil.EMPTY_STRING_ARRAY open val possibleNamesFromExpression: Array<String> get() = ArrayUtil.EMPTY_STRING_ARRAY
abstract fun getPossibleTypes(builder: FunctionBuilder): List<JetType> abstract fun getPossibleTypes(builder: CallableBuilder): List<JetType>
protected fun JetType.getPossibleSupertypes(variance: Variance): List<JetType> { protected fun JetType.getPossibleSupertypes(variance: Variance): List<JetType> {
val single = Collections.singletonList(this) val single = Collections.singletonList(this)
@@ -61,9 +61,32 @@ class ParameterInfo(
val preferredName: String? = null val preferredName: String? = null
) )
class FunctionInfo ( enum class CallableKind {
FUNCTION
PROPERTY
}
class CallableInfo (
val name: String, val name: String,
val kind: CallableKind,
val receiverTypeInfo: TypeInfo, val receiverTypeInfo: TypeInfo,
val returnTypeInfo: TypeInfo, val returnTypeInfo: TypeInfo,
val parameterInfos: List<ParameterInfo> = Collections.emptyList() val parameterInfos: List<ParameterInfo> = Collections.emptyList()
) ) {
{
if (kind == CallableKind.PROPERTY) assert (parameterInfos.isEmpty(), "$kind: Parameters are not allowed")
}
}
fun createFunctionInfo(name: String,
receiverTypeInfo: TypeInfo,
returnTypeInfo: TypeInfo,
parameterInfos: List<ParameterInfo> = Collections.emptyList()): CallableInfo {
return CallableInfo(name, CallableKind.FUNCTION, receiverTypeInfo, returnTypeInfo, parameterInfos)
}
fun createPropertyInfo(name: String,
receiverTypeInfo: TypeInfo,
returnTypeInfo: TypeInfo): CallableInfo {
return CallableInfo(name, CallableKind.PROPERTY, receiverTypeInfo, returnTypeInfo)
}
@@ -37,6 +37,6 @@ public object CreateBinaryOperationActionFactory: JetSingleIntentionActionFactor
else -> TypeInfo(callExpr, Variance.OUT_VARIANCE) else -> TypeInfo(callExpr, Variance.OUT_VARIANCE)
} }
val parameters = Collections.singletonList(ParameterInfo(TypeInfo(argumentExpr, Variance.IN_VARIANCE))) val parameters = Collections.singletonList(ParameterInfo(TypeInfo(argumentExpr, Variance.IN_VARIANCE)))
return CreateFunctionFromUsageFix(callExpr, FunctionInfo(operationName, receiverType, returnType, parameters)) return CreateFunctionFromUsageFix(callExpr, createFunctionInfo(operationName, receiverType, returnType, parameters))
} }
} }
@@ -33,6 +33,6 @@ object CreateComponentFunctionActionFactory : JetSingleIntentionActionFactory()
val entry = entries[componentNumber] val entry = entries[componentNumber]
val returnType = TypeInfo(entry, Variance.OUT_VARIANCE) val returnType = TypeInfo(entry, Variance.OUT_VARIANCE)
return CreateFunctionFromUsageFix(multiDeclaration!!, FunctionInfo(name.getIdentifier(), ownerType, returnType)) return CreateFunctionFromUsageFix(multiDeclaration!!, createFunctionInfo(name.getIdentifier(), ownerType, returnType))
} }
} }
@@ -47,6 +47,6 @@ object CreateFunctionFromCallActionFactory : JetSingleIntentionActionFactory() {
} }
val returnType = TypeInfo(fullCallExpr, Variance.OUT_VARIANCE) val returnType = TypeInfo(fullCallExpr, Variance.OUT_VARIANCE)
return CreateFunctionFromUsageFix(callExpr, FunctionInfo(calleeExpr.getReferencedName(), receiverType, returnType, parameters)) return CreateFunctionFromUsageFix(callExpr, createFunctionInfo(calleeExpr.getReferencedName(), receiverType, returnType, parameters))
} }
} }
@@ -14,15 +14,15 @@ import org.jetbrains.jet.plugin.refactoring.chooseContainerElementIfNecessary
import org.jetbrains.jet.plugin.refactoring.getExtractionContainers import org.jetbrains.jet.plugin.refactoring.getExtractionContainers
import org.jetbrains.jet.lang.psi.JetClassBody import org.jetbrains.jet.lang.psi.JetClassBody
public class CreateFunctionFromUsageFix(element: PsiElement, val functionInfo: FunctionInfo) : CreateFromUsageFixBase(element) { public class CreateFunctionFromUsageFix(element: PsiElement, val functionInfo: CallableInfo) : CreateFromUsageFixBase(element) {
override fun getText(): String { override fun getText(): String {
return JetBundle.message("create.function.from.usage", functionInfo.name) return JetBundle.message("create.function.from.usage", functionInfo.name)
} }
override fun invoke(project: Project, editor: Editor?, file: JetFile?) { override fun invoke(project: Project, editor: Editor?, file: JetFile?) {
val functionBuilder = FunctionBuilderConfiguration(functionInfo, file!!, editor!!).createBuilder() val functionBuilder = CallableBuilderConfiguration(functionInfo, file!!, editor!!).createBuilder()
fun runBuilder(placement: FunctionPlacement) { fun runBuilder(placement: CallablePlacement) {
functionBuilder.placement = placement functionBuilder.placement = placement
CommandProcessor.getInstance().executeCommand(project, { functionBuilder.build() }, getText(), null) CommandProcessor.getInstance().executeCommand(project, { functionBuilder.build() }, getText(), null)
} }
@@ -35,7 +35,7 @@ public class CreateFunctionFromUsageFix(element: PsiElement, val functionInfo: F
DescriptorToDeclarationUtil.getDeclaration(file, descriptor) as JetClassOrObject DescriptorToDeclarationUtil.getDeclaration(file, descriptor) as JetClassOrObject
} }
chooseContainerElementIfNecessary(receiverTypeCandidates, editor, popupTitle, false, toPsi) { chooseContainerElementIfNecessary(receiverTypeCandidates, editor, popupTitle, false, toPsi) {
runBuilder(FunctionPlacement.WithReceiver(it)) runBuilder(CallablePlacement.WithReceiver(it))
} }
} }
else { else {
@@ -43,7 +43,7 @@ public class CreateFunctionFromUsageFix(element: PsiElement, val functionInfo: F
chooseContainerElementIfNecessary(element.getExtractionContainers(), editor, popupTitle, true, { it }) { chooseContainerElementIfNecessary(element.getExtractionContainers(), editor, popupTitle, true, { it }) {
val container = if (it is JetClassBody) it.getParent() as JetClassOrObject else it val container = if (it is JetClassBody) it.getParent() as JetClassOrObject else it
runBuilder(FunctionPlacement.NoReceiver(container)) runBuilder(CallablePlacement.NoReceiver(container))
} }
} }
} }
@@ -18,6 +18,6 @@ object CreateGetFunctionActionFactory : JetSingleIntentionActionFactory() {
} }
val returnType = TypeInfo(accessExpr, Variance.OUT_VARIANCE) val returnType = TypeInfo(accessExpr, Variance.OUT_VARIANCE)
return CreateFunctionFromUsageFix(accessExpr, FunctionInfo("get", arrayType, returnType, parameters)) return CreateFunctionFromUsageFix(accessExpr, createFunctionInfo("get", arrayType, returnType, parameters))
} }
} }
@@ -17,6 +17,6 @@ object CreateHasNextFunctionActionFactory : JetSingleIntentionActionFactory() {
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null
val returnType = TypeInfo(KotlinBuiltIns.getInstance().getBooleanType(), Variance.OUT_VARIANCE) val returnType = TypeInfo(KotlinBuiltIns.getInstance().getBooleanType(), Variance.OUT_VARIANCE)
return CreateFunctionFromUsageFix(forExpr, FunctionInfo("hasNext", ownerType, returnType)) return CreateFunctionFromUsageFix(forExpr, createFunctionInfo("hasNext", ownerType, returnType))
} }
} }
@@ -28,6 +28,6 @@ object CreateInvokeFunctionActionFactory : JetSingleIntentionActionFactory() {
} }
val returnType = TypeInfo(callExpr, Variance.OUT_VARIANCE) val returnType = TypeInfo(callExpr, Variance.OUT_VARIANCE)
return CreateFunctionFromUsageFix(callExpr, FunctionInfo("invoke", receiverType, returnType, parameters)) return CreateFunctionFromUsageFix(callExpr, createFunctionInfo("invoke", receiverType, returnType, parameters))
} }
} }
@@ -31,6 +31,6 @@ object CreateIteratorFunctionActionFactory : JetSingleIntentionActionFactory() {
val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType) val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType)
val newReturnJetType = JetTypeImpl(returnJetType.getAnnotations(), returnJetType.getConstructor(), returnJetType.isNullable(), returnJetTypeArguments, returnJetType.getMemberScope()) val newReturnJetType = JetTypeImpl(returnJetType.getAnnotations(), returnJetType.getConstructor(), returnJetType.isNullable(), returnJetTypeArguments, returnJetType.getMemberScope())
val returnType = TypeInfo(newReturnJetType, Variance.OUT_VARIANCE) val returnType = TypeInfo(newReturnJetType, Variance.OUT_VARIANCE)
return CreateFunctionFromUsageFix(forExpr, FunctionInfo("iterator", iterableType, returnType)) return CreateFunctionFromUsageFix(forExpr, createFunctionInfo("iterator", iterableType, returnType))
} }
} }
@@ -18,6 +18,6 @@ object CreateNextFunctionActionFactory : JetSingleIntentionActionFactory() {
val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetForExpression>()) ?: return null
val variableExpr: JetExpression = ((forExpr.getLoopParameter() ?: forExpr.getMultiParameter()) ?: return null) as JetExpression val variableExpr: JetExpression = ((forExpr.getLoopParameter() ?: forExpr.getMultiParameter()) ?: return null) as JetExpression
val returnType = TypeInfo(variableExpr, Variance.OUT_VARIANCE) val returnType = TypeInfo(variableExpr, Variance.OUT_VARIANCE)
return CreateFunctionFromUsageFix(forExpr, FunctionInfo("next", ownerType, returnType)) return CreateFunctionFromUsageFix(forExpr, createFunctionInfo("next", ownerType, returnType))
} }
} }
@@ -26,6 +26,6 @@ object CreateSetFunctionActionFactory : JetSingleIntentionActionFactory() {
parameters.add(ParameterInfo(valType, "value")) parameters.add(ParameterInfo(valType, "value"))
val returnType = TypeInfo(KotlinBuiltIns.getInstance().getUnitType(), Variance.OUT_VARIANCE) val returnType = TypeInfo(KotlinBuiltIns.getInstance().getUnitType(), Variance.OUT_VARIANCE)
return CreateFunctionFromUsageFix(accessExpr, FunctionInfo("set", arrayType, returnType, parameters)) return CreateFunctionFromUsageFix(accessExpr, createFunctionInfo("set", arrayType, returnType, parameters))
} }
} }
@@ -22,6 +22,6 @@ public object CreateUnaryOperationActionFactory: JetSingleIntentionActionFactory
val receiverType = TypeInfo(receiverExpr, Variance.IN_VARIANCE) val receiverType = TypeInfo(receiverExpr, Variance.IN_VARIANCE)
val returnType = if (incDec) TypeInfo.ByReceiverType(Variance.OUT_VARIANCE) else TypeInfo(callExpr, Variance.OUT_VARIANCE) val returnType = if (incDec) TypeInfo.ByReceiverType(Variance.OUT_VARIANCE) else TypeInfo(callExpr, Variance.OUT_VARIANCE)
return CreateFunctionFromUsageFix(callExpr, FunctionInfo(operationName.asString(), receiverType, returnType)) return CreateFunctionFromUsageFix(callExpr, createFunctionInfo(operationName.asString(), receiverType, returnType))
} }
} }
@@ -108,7 +108,7 @@ data class ExtractionData(
val originalStartOffset = originalElements.first?.let { e -> e.getTextRange()!!.getStartOffset() } val originalStartOffset = originalElements.first?.let { e -> e.getTextRange()!!.getStartOffset() }
private val itFakeDeclaration by Delegates.lazy { JetPsiFactory(originalFile).createParameter("it", "Any?") } private val itFakeDeclaration by Delegates.lazy { JetPsiFactory(originalFile).createParameter("it: Any?") }
val refOffsetToDeclaration by Delegates.lazy { val refOffsetToDeclaration by Delegates.lazy {
fun isExtractableIt(descriptor: DeclarationDescriptor, context: BindingContext): Boolean { fun isExtractableIt(descriptor: DeclarationDescriptor, context: BindingContext): Boolean {