172: Revert "Quick Fixes: Support cross-language "Create from Usage" with Kotlin target"

This reverts commit 908bf71
This commit is contained in:
Vyacheslav Gerasimov
2018-01-18 02:20:03 +03:00
committed by Nikolay Krasko
parent 747f397f40
commit 8d3f71a04f
42 changed files with 3908 additions and 501 deletions
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.psi.CREATEBYPATTERN_MAY_NOT_REFORMAT
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
abstract class KotlinQuickFixAction<out T : PsiElement>(element: T) : IntentionAction {
private val elementPointer = element.createSmartPointer()
protected val element: T?
get() = elementPointer.element
final override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
if (ApplicationManager.getApplication().isUnitTestMode) {
CREATEBYPATTERN_MAY_NOT_REFORMAT = true
}
try {
val element = element ?: return false
return element.isValid &&
!element.project.isDisposed &&
(file.manager.isInProject(file) || file is KtCodeFragment) &&
(file is KtFile) &&
isAvailable(project, editor, file)
}
finally {
CREATEBYPATTERN_MAY_NOT_REFORMAT = false
}
}
protected open fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
return true
}
final override fun invoke(project: Project, editor: Editor?, file: PsiFile) {
val element = element ?: return
if (file is KtFile && FileModificationService.getInstance().prepareFileForWrite(element.containingFile)) {
invoke(project, editor, file)
}
}
protected abstract fun invoke(project: Project, editor: Editor?, file: KtFile)
override fun startInWriteAction() = true
}
@@ -464,34 +464,16 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val psiFactory = KtPsiFactory(currentFile)
val modifiers = buildString {
val modifierList = callableInfo.modifierList?.copied() ?: psiFactory.createEmptyModifierList()
val visibilityKeyword = modifierList.visibilityModifierType()
if (visibilityKeyword == null) {
val defaultVisibility =
if (callableInfo.isAbstract) ""
else if (containingElement is KtClassOrObject
&& !(containingElement is KtClass && containingElement.isInterface())
&& containingElement.isAncestor(config.originalElement)
&& callableInfo.kind != CallableKind.CONSTRUCTOR) "private "
else if (isExtension) "private "
else ""
append(defaultVisibility)
}
// TODO: Get rid of isAbstract
if (callableInfo.isAbstract
&& containingElement is KtClass
&& !containingElement.isInterface()
&& !modifierList.hasModifier(KtTokens.ABSTRACT_KEYWORD)) {
modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD)
}
val text = modifierList.normalize().text
if (text.isNotEmpty()) {
append("$text ")
}
}
val modifiers =
if (callableInfo.isAbstract) {
if (containingElement is KtClass && containingElement.isInterface()) "" else "abstract "
}
else if (containingElement is KtClassOrObject
&& !(containingElement is KtClass && containingElement.isInterface())
&& containingElement.isAncestor(config.originalElement)
&& callableInfo.kind != CallableKind.CONSTRUCTOR) "private "
else if (isExtension) "private "
else ""
val isExpectClassMember by lazy {
containingElement is KtClassOrObject && containingElement.resolveToDescriptorIfAny()?.isExpect ?: false
@@ -500,7 +482,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val declaration: KtNamedDeclaration = when (callableInfo.kind) {
CallableKind.FUNCTION, CallableKind.CONSTRUCTOR -> {
val body = when {
callableInfo is ConstructorInfo -> if (callableInfo.withBody) "{\n\n}" else ""
callableInfo.kind == CallableKind.CONSTRUCTOR -> ""
callableInfo.isAbstract -> ""
containingElement is KtClass && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
containingElement is KtObjectDeclaration && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
@@ -508,7 +490,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
&& containingElement.parent.parent is KtClass
&& (containingElement.parent.parent as KtClass).hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
isExpectClassMember -> ""
else -> "{\n\n}"
else -> "{}"
}
@Suppress("USELESS_CAST") // KT-10755
@@ -574,11 +556,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
return assignmentToReplace.replace(declaration) as KtCallableDeclaration
}
val container = if (containingElement is KtClass && callableInfo.isForCompanion) {
containingElement.getOrCreateCompanionObject()
}
else containingElement
val declarationInPlace = placeDeclarationInContainer(declaration, container, config.originalElement, jetFileToEdit)
val declarationInPlace = placeDeclarationInContainer(declaration, containingElement, config.originalElement, jetFileToEdit)
if (declarationInPlace is KtSecondaryConstructor) {
val containingClass = declarationInPlace.containingClassOrObject!!
@@ -615,11 +593,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
}
private fun setupTypeReferencesForShortening(declaration: KtNamedDeclaration,
parameterTypeExpressions: List<TypeExpression>) {
parameterTypeExpressions: List<TypeExpression>): List<KtElement> {
val typeRefsToShorten = ArrayList<KtElement>()
if (config.isExtension) {
val receiverTypeText = receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap).first()
val replacingTypeRef = KtPsiFactory(declaration).createType(receiverTypeText)
(declaration as KtCallableDeclaration).setReceiverTypeReference(replacingTypeRef)!!
val newTypeRef = (declaration as KtCallableDeclaration).setReceiverTypeReference(replacingTypeRef)!!
typeRefsToShorten.add(newTypeRef)
}
val returnTypeRefs = declaration.getReturnTypeReferences()
@@ -630,6 +611,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
if (returnType != null) {
// user selected a given type
replaceWithLongerName(returnTypeRefs, returnType)
typeRefsToShorten.addAll(declaration.getReturnTypeReferences())
}
}
@@ -648,6 +630,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
}
}
}
val expandedValueParameters = declaration.getValueParameters()
parameterIndicesToShorten.mapNotNullTo(typeRefsToShorten) { expandedValueParameters[it].typeReference }
return typeRefsToShorten
}
private fun postprocessDeclaration(declaration: KtNamedDeclaration) {
@@ -661,12 +648,15 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
}
private fun setupDeclarationBody(func: KtDeclarationWithBody) {
if (func !is KtNamedFunction && func !is KtPropertyAccessor) return
if (skipReturnType && callableInfo is FunctionInfo && callableInfo.preferEmptyBody) return
val oldBody = func.bodyExpression ?: return
val templateKind = when (func) {
is KtSecondaryConstructor -> TemplateKind.SECONDARY_CONSTRUCTOR
is KtNamedFunction, is KtPropertyAccessor -> TemplateKind.FUNCTION
else -> throw AssertionError("Unexpected declaration: " + func.getElementTextWithContext())
}
val bodyText = getFunctionBodyTextFromTemplate(
func.project,
TemplateKind.FUNCTION,
templateKind,
if (callableInfo.name.isNotEmpty()) callableInfo.name else null,
if (skipReturnType) "Unit" else (func as? KtFunction)?.typeReference?.text ?: "",
receiverClassDescriptor?.importableFqName ?: receiverClassDescriptor?.name?.let { FqName.topLevel(it) }
@@ -773,7 +763,13 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
// add parameter name to the template
val possibleNamesFromExpression = parameter.typeInfo.getPossibleNamesFromExpression(currentFileContext)
val possibleNames = arrayOf(*parameter.nameSuggestions.toTypedArray(), *possibleNamesFromExpression)
val preferredName = parameter.preferredName
val possibleNames = if (preferredName != null) {
arrayOf(preferredName, *possibleNamesFromExpression)
}
else {
possibleNamesFromExpression
}
// figure out suggested names for each type option
val parameterTypeToNamesMap = HashMap<String, Array<String>>()
@@ -967,14 +963,6 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
if (newDeclaration is KtProperty) {
newDeclaration.getter?.let { setupDeclarationBody(it) }
if (newDeclaration.getter == null
&& newDeclaration.initializer == null
&& callableInfo is PropertyInfo
&& callableInfo.withInitializer
&& !callableInfo.isLateinitPreferred) {
newDeclaration.initializer = KtPsiFactory(newDeclaration).createExpression("TODO(\"initialize me\")")
}
}
val callElement = config.originalElement as? KtCallElement
@@ -985,9 +973,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
CodeStyleManager.getInstance(project).reformat(newDeclaration)
// change short type names to fully qualified ones (to be shortened below)
setupTypeReferencesForShortening(newDeclaration, parameterTypeExpressions)
val typeRefsToShorten = setupTypeReferencesForShortening(newDeclaration, parameterTypeExpressions)
if (!transformToJavaMemberIfApplicable(newDeclaration)) {
elementsToShorten.add(newDeclaration)
elementsToShorten.addAll(typeRefsToShorten)
setupEditor(newDeclaration)
}
}
@@ -1114,24 +1102,17 @@ internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
}
container is KtClassOrObject -> {
var sibling: PsiElement? = container.declarations.lastOrNull { it::class == declaration::class }
if (sibling == null && declaration is KtProperty) {
sibling = container.getBody()?.lBrace
}
insertMember(null, container, declaration, sibling)
insertMember(null, container, declaration, container.declarations.lastOrNull())
}
else -> throw AssertionError("Invalid containing element: ${container.text}")
}
if (declaration !is KtPrimaryConstructor) {
val parent = declarationInPlace.parent
calcNecessaryEmptyLines(declarationInPlace, false).let {
if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace)
}
calcNecessaryEmptyLines(declarationInPlace, true).let {
if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace)
}
val parent = declarationInPlace.parent
calcNecessaryEmptyLines(declarationInPlace, false).let {
if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace)
}
calcNecessaryEmptyLines(declarationInPlace, true).let {
if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace)
}
return declarationInPlace
}
@@ -70,10 +70,6 @@ abstract class TypeInfo(val variance: Variance) {
(builder.placement as CallablePlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance, builder)
}
class ByExplicitCandidateTypes(val types: List<KotlinType>) : TypeInfo(Variance.INVARIANT) {
override fun getPossibleTypes(builder: CallableBuilder) = types
}
abstract class DelegatingTypeInfo(val delegate: TypeInfo): TypeInfo(delegate.variance) {
override val substitutionsAllowed: Boolean = delegate.substitutionsAllowed
override fun getPossibleNamesFromExpression(bindingContext: BindingContext) = delegate.getPossibleNamesFromExpression(bindingContext)
@@ -159,10 +155,8 @@ fun TypeInfo.ofThis() = TypeInfo.OfThis(this)
*/
class ParameterInfo(
val typeInfo: TypeInfo,
val nameSuggestions: List<String>
) {
constructor(typeInfo: TypeInfo, preferredName: String? = null): this(typeInfo, listOfNotNull(preferredName))
}
val preferredName: String? = null
)
enum class CallableKind {
FUNCTION,
@@ -177,9 +171,7 @@ abstract class CallableInfo (
val returnTypeInfo: TypeInfo,
val possibleContainers: List<KtElement>,
val typeParameterInfos: List<TypeInfo>,
val isAbstract: Boolean = false,
val isForCompanion: Boolean = false,
val modifierList: KtModifierList? = null
val isAbstract: Boolean = false
) {
abstract val kind: CallableKind
abstract val parameterInfos: List<ParameterInfo>
@@ -197,11 +189,8 @@ class FunctionInfo(name: String,
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
val isOperator: Boolean = false,
val isInfix: Boolean = false,
isAbstract: Boolean = false,
isForCompanion: Boolean = false,
modifierList: KtModifierList? = null,
val preferEmptyBody: Boolean = false
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract, isForCompanion, modifierList) {
isAbstract: Boolean = false
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) {
override val kind: CallableKind get() = CallableKind.FUNCTION
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = FunctionInfo(
@@ -217,12 +206,8 @@ class FunctionInfo(name: String,
)
}
class ClassWithPrimaryConstructorInfo(
val classInfo: ClassInfo,
expectedTypeInfo: TypeInfo,
modifierList: KtModifierList? = null
): CallableInfo(
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false, modifierList = modifierList
class ClassWithPrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo(
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false
) {
override val kind: CallableKind get() = CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
override val parameterInfos: List<ParameterInfo> get() = classInfo.parameterInfos
@@ -233,10 +218,8 @@ class ClassWithPrimaryConstructorInfo(
class ConstructorInfo(
override val parameterInfos: List<ParameterInfo>,
val targetClass: PsiElement,
val isPrimary: Boolean = false,
modifierList: KtModifierList? = null,
val withBody: Boolean = false
): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false, modifierList = modifierList) {
val isPrimary: Boolean = false
): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false) {
override val kind: CallableKind get() = CallableKind.CONSTRUCTOR
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
@@ -249,23 +232,12 @@ class PropertyInfo(name: String,
possibleContainers: List<KtElement> = Collections.emptyList(),
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
isAbstract: Boolean = false,
val isLateinitPreferred: Boolean = false,
isForCompanion: Boolean = false,
modifierList: KtModifierList? = null,
val withInitializer: Boolean = false
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract, isForCompanion, modifierList) {
val isLateinitPreferred: Boolean = false
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) {
override val kind: CallableKind get() = CallableKind.PROPERTY
override val parameterInfos: List<ParameterInfo> get() = Collections.emptyList()
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) =
copyProperty(receiverTypeInfo, possibleContainers, isAbstract)
fun copyProperty(
receiverTypeInfo: TypeInfo = this.receiverTypeInfo,
possibleContainers: List<KtElement> = this.possibleContainers,
isAbstract: Boolean = this.isAbstract,
isLateinitPreferred: Boolean = this.isLateinitPreferred
) = PropertyInfo(
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = PropertyInfo(
name,
receiverTypeInfo,
returnTypeInfo,
@@ -273,9 +245,6 @@ class PropertyInfo(name: String,
possibleContainers,
typeParameterInfos,
isAbstract,
isLateinitPreferred,
isForCompanion,
modifierList,
withInitializer
isLateinitPreferred
)
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.psi.psiUtil.startOffset
fun setupEditorSelection(editor: Editor, declaration: KtNamedDeclaration) {
val caretModel = editor.caretModel
val selectionModel = editor.selectionModel
if (declaration is KtSecondaryConstructor) {
caretModel.moveToOffset(declaration.getConstructorKeyword().endOffset)
}
else {
caretModel.moveToOffset(declaration.nameIdentifier!!.endOffset)
}
fun positionBetween(left: PsiElement, right: PsiElement) {
val from = left.siblings(withItself = false, forward = true).firstOrNull { it !is PsiWhiteSpace } ?: return
val to = right.siblings(withItself = false, forward = false).firstOrNull { it !is PsiWhiteSpace } ?: return
val startOffset = from.startOffset
val endOffset = to.endOffset
caretModel.moveToOffset(endOffset)
selectionModel.setSelection(startOffset, endOffset)
}
when (declaration) {
is KtNamedFunction, is KtSecondaryConstructor -> {
((declaration as KtFunction).bodyExpression as? KtBlockExpression)?.let {
positionBetween(it.lBrace!!, it.rBrace!!)
}
}
is KtClassOrObject -> {
caretModel.moveToOffset(declaration.startOffset)
}
is KtProperty -> {
caretModel.moveToOffset(declaration.endOffset)
}
}
editor.scrollingModel.scrollToCaret(ScrollType.CENTER)
}
@@ -0,0 +1,216 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.isAbstract
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import java.util.*
class CreateCallableFromUsageFix<E : KtElement>(
originalExpression: E,
callableInfos: List<CallableInfo>
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, false)
class CreateExtensionCallableFromUsageFix<E : KtElement>(
originalExpression: E,
callableInfos: List<CallableInfo>
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, true), LowPriorityAction
abstract class CreateCallableFromUsageFixBase<E : KtElement>(
originalExpression: E,
private val callableInfos: List<CallableInfo>,
val isExtension: Boolean
) : CreateFromUsageFixBase<E>(originalExpression) {
init {
assert (callableInfos.isNotEmpty()) { "No CallableInfos: ${originalExpression.getElementTextWithContext()}" }
if (callableInfos.size > 1) {
val receiverSet = callableInfos.mapTo(HashSet<TypeInfo>()) { it.receiverTypeInfo }
if (receiverSet.size > 1) throw AssertionError("All functions must have common receiver: $receiverSet")
val possibleContainerSet = callableInfos.mapTo(HashSet<List<KtElement>>()) { it.possibleContainers }
if (possibleContainerSet.size > 1) throw AssertionError("All functions must have common containers: $possibleContainerSet")
}
}
private fun getDeclaration(descriptor: ClassifierDescriptor, project: Project): PsiElement? {
if (descriptor is FunctionClassDescriptor) {
val psiFactory = KtPsiFactory(project)
val syntheticClass = psiFactory.createClass(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(descriptor))
return psiFactory.createAnalyzableFile("${descriptor.name.asString()}.kt", "", element!!).add(syntheticClass)
}
return DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor)
}
private fun getDeclarationIfApplicable(project: Project, candidate: TypeCandidate): PsiElement? {
val descriptor = candidate.theType.constructor.declarationDescriptor ?: return null
val declaration = getDeclaration(descriptor, project) ?: return null
if (declaration !is KtClassOrObject && declaration !is KtTypeParameter && declaration !is PsiClass) return null
return if (isExtension || declaration.canRefactor()) declaration else null
}
override fun getText(): String {
val element = element ?: return ""
val receiverTypeInfo = callableInfos.first().receiverTypeInfo
val renderedCallables = callableInfos.map {
buildString {
if (it.isAbstract) {
append("abstract ")
}
val kind = when (it.kind) {
CallableKind.FUNCTION -> "function"
CallableKind.PROPERTY -> "property"
CallableKind.CONSTRUCTOR -> "secondary constructor"
else -> throw AssertionError("Unexpected callable info: $it")
}
append(kind)
if (it.name.isNotEmpty()) {
append(" '")
val receiverType = if (!receiverTypeInfo.isOfThis) {
CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension)
.createBuilder()
.computeTypeCandidates(receiverTypeInfo)
.firstOrNull()
?.theType
}
else null
if (receiverType != null) {
if (isExtension) {
val receiverTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(receiverType)
val isFunctionType = receiverType.constructor.declarationDescriptor is FunctionClassDescriptor
append(if (isFunctionType) "($receiverTypeText)" else receiverTypeText).append('.')
}
else {
receiverType.constructor.declarationDescriptor?.let {
append(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderClassifierName(it)).append('.')
}
}
}
append("${it.name}'")
}
}
}
return StringBuilder().apply {
append("Create ")
val receiverInfo = receiverTypeInfo
if (!callableInfos.any { it.isAbstract }) {
if (isExtension) {
append("extension ")
}
else if (receiverInfo !is TypeInfo.Empty) {
append("member ")
}
}
renderedCallables.joinTo(this)
}.toString()
}
override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
val element = element ?: return false
val receiverInfo = callableInfos.first().receiverTypeInfo
if (receiverInfo is TypeInfo.Empty) {
if (callableInfos.any { it is PropertyInfo && it.possibleContainers.isEmpty() }) return false
return !isExtension
}
// TODO: Remove after companion object extensions are supported
if (isExtension && receiverInfo.staticContextRequired) return false
val callableBuilder = CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension).createBuilder()
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfos.first().receiverTypeInfo)
val propertyInfo = callableInfos.firstOrNull { it is PropertyInfo } as PropertyInfo?
val isFunction = callableInfos.any { it.kind == CallableKind.FUNCTION }
return receiverTypeCandidates.any {
val declaration = getDeclarationIfApplicable(project, it)
val insertToJavaInterface = declaration is PsiClass && declaration.isInterface
when {
!isExtension && propertyInfo != null && insertToJavaInterface && (!receiverInfo.staticContextRequired || propertyInfo.writable) ->
false
isFunction && insertToJavaInterface && receiverInfo.staticContextRequired ->
false
!isExtension && declaration is KtTypeParameter -> false
propertyInfo != null && !propertyInfo.isAbstract && declaration is KtClass && declaration.isInterface() -> false
else ->
declaration != null
}
}
}
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val element = element ?: return
val callableInfo = callableInfos.first()
val callableBuilder =
CallableBuilderConfiguration(callableInfos, element as KtElement, file, editor!!, isExtension).createBuilder()
fun runBuilder(placement: CallablePlacement) {
callableBuilder.placement = placement
project.executeCommand(text) { callableBuilder.build() }
}
if (callableInfo is ConstructorInfo) {
runBuilder(CallablePlacement.NoReceiver(callableInfo.targetClass))
return
}
val popupTitle = "Choose target class or interface"
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo).let {
if (callableInfo.isAbstract) it.filter { it.theType.isAbstract() } else it
}
if (receiverTypeCandidates.isNotEmpty()) {
val containers = receiverTypeCandidates
.mapNotNull { candidate -> getDeclarationIfApplicable(project, candidate)?.let { candidate to it } }
chooseContainerElementIfNecessary(containers, editor, popupTitle, false, { it.second }) {
runBuilder(CallablePlacement.WithReceiver(it.first))
}
}
else {
assert(callableInfo.receiverTypeInfo is TypeInfo.Empty) {
"No receiver type candidates: ${element.text} in ${file.text}"
}
chooseContainerElementIfNecessary(callableInfo.possibleContainers, editor, popupTitle, true, { it }) {
val container = if (it is KtClassBody) it.parent as KtClassOrObject else it
runBuilder(CallablePlacement.NoReceiver(container))
}
}
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
import com.intellij.psi.SmartPsiElementPointer
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionFactoryWithDelegate
import org.jetbrains.kotlin.idea.quickfix.QuickFixWithDelegateFactory
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.PropertyInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterFromUsageFix
import org.jetbrains.kotlin.psi.KtElement
import java.util.*
abstract class CreateCallableMemberFromUsageFactory<E : KtElement>(
private val extensionsSupported: Boolean = true
) : KotlinIntentionActionFactoryWithDelegate<E, List<CallableInfo>>() {
private fun newCallableQuickFix(
originalElementPointer: SmartPsiElementPointer<E>,
priority: IntentionActionPriority,
quickFixDataFactory: () -> List<CallableInfo>?,
quickFixFactory: (E, List<CallableInfo>) -> CreateFromUsageFixBase<E>?
): QuickFixWithDelegateFactory {
return QuickFixWithDelegateFactory(priority) {
val data = quickFixDataFactory().orEmpty()
val originalElement = originalElementPointer.element
if (data.isNotEmpty() && originalElement != null) quickFixFactory(originalElement, data) else null
}
}
protected open fun createCallableInfo(element: E, diagnostic: Diagnostic): CallableInfo? = null
override fun extractFixData(element: E, diagnostic: Diagnostic): List<CallableInfo>
= listOfNotNull(createCallableInfo(element, diagnostic))
override fun createFixes(
originalElementPointer: SmartPsiElementPointer<E>,
diagnostic: Diagnostic,
quickFixDataFactory: () -> List<CallableInfo>?
): List<QuickFixWithDelegateFactory> {
val fixes = ArrayList<QuickFixWithDelegateFactory>(3)
newCallableQuickFix(originalElementPointer, IntentionActionPriority.NORMAL, quickFixDataFactory) { element, data ->
CreateCallableFromUsageFix(element, data)
}.let { fixes.add(it) }
newCallableQuickFix(originalElementPointer, IntentionActionPriority.NORMAL, quickFixDataFactory) f@ { element, data ->
(data.singleOrNull() as? PropertyInfo)?.let {
CreateParameterFromUsageFix.createFixForPrimaryConstructorPropertyParameter(element, it)
}
}.let { fixes.add(it) }
if (extensionsSupported) {
newCallableQuickFix(originalElementPointer, IntentionActionPriority.LOW, quickFixDataFactory) { element, data ->
if (data.any { it.isAbstract }) return@newCallableQuickFix null
CreateExtensionCallableFromUsageFix(element, data)
}.let { fixes.add(it) }
}
return fixes
}
}
@@ -1,390 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.quickfix.crossLanguage
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.intention.QuickFixFactory
import com.intellij.lang.jvm.*
import com.intellij.lang.jvm.actions.*
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.appendModifier
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.CreateCallableFromUsageFix
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.lazy.JavaResolverComponents
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver
import org.jetbrains.kotlin.load.java.lazy.child
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaTypeParameterDescriptor
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeAttributes
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.supertypes
class KotlinElementActionsFactory : JvmElementActionsFactory() {
companion object {
val javaPsiModifiersMapping = mapOf(
JvmModifier.PRIVATE to KtTokens.PRIVATE_KEYWORD,
JvmModifier.PUBLIC to KtTokens.PUBLIC_KEYWORD,
JvmModifier.PROTECTED to KtTokens.PUBLIC_KEYWORD,
JvmModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD
)
}
private class FakeExpressionFromParameter(private val psiParam: PsiParameter) : PsiReferenceExpressionImpl() {
override fun getText(): String = psiParam.name!!
override fun getProject(): Project = psiParam.project
override fun getParent(): PsiElement = psiParam.parent
override fun getType(): PsiType? = psiParam.type
override fun isValid(): Boolean = true
override fun getContainingFile(): PsiFile = psiParam.containingFile
override fun getReferenceName(): String? = psiParam.name
override fun resolve(): PsiElement? = psiParam
}
private class ModifierBuilder(
private val targetContainer: KtElement,
private val allowJvmStatic: Boolean = true
) {
private val psiFactory = KtPsiFactory(targetContainer.project)
val modifierList = psiFactory.createEmptyModifierList()
private fun JvmModifier.transformAndAppend(): Boolean {
javaPsiModifiersMapping[this]?.let {
modifierList.appendModifier(it)
return true
}
when (this) {
JvmModifier.STATIC -> {
if (allowJvmStatic && targetContainer is KtClassOrObject) {
addAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME)
}
}
JvmModifier.ABSTRACT -> modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD)
JvmModifier.FINAL -> modifierList.appendModifier(KtTokens.FINAL_KEYWORD)
else -> return false
}
return true
}
var isValid = true
private set
fun addJvmModifier(modifier: JvmModifier) {
isValid = isValid && modifier.transformAndAppend()
}
fun addJvmModifiers(modifiers: Iterable<JvmModifier>) {
modifiers.forEach { addJvmModifier(it) }
}
fun addAnnotation(fqName: FqName) {
if (!isValid) return
modifierList.add(psiFactory.createAnnotationEntry("@${fqName.asString()}"))
}
}
class CreatePropertyFix(
private val targetClass: JvmClass,
contextElement: KtElement,
propertyInfo: PropertyInfo
) : CreateCallableFromUsageFix<KtElement>(contextElement, listOf(propertyInfo)) {
override fun getFamilyName() = "Add property"
override fun getText(): String {
val info = callableInfos.first() as PropertyInfo
return buildString {
append("Add '")
if (info.isLateinitPreferred) {
append("lateinit ")
}
append(if (info.writable) "var" else "val")
append("' property '${info.name}' to '${targetClass.name}'")
}
}
}
private fun JvmClass.toKtClassOrFile(): KtElement? {
val psi = sourceElement
return when (psi) {
is KtClassOrObject -> psi
is KtLightClassForSourceDeclaration -> psi.kotlinOrigin
is KtLightClassForFacade -> psi.files.firstOrNull()
else -> null
}
}
private inline fun <reified T : KtElement> JvmElement.toKtElement() = sourceElement?.unwrapped as? T
private fun fakeParametersExpressions(parameters: List<JvmParameter>, project: Project): Array<PsiExpression>? =
when {
parameters.isEmpty() -> emptyArray()
else -> JavaPsiFacade
.getElementFactory(project)
.createParameterList(
parameters.map { it.name }.toTypedArray(),
parameters.map { it.type as? PsiType ?: return null }.toTypedArray()
)
.parameters
.map(::FakeExpressionFromParameter)
.toTypedArray()
}
private fun PsiType.collectTypeParameters(): List<PsiTypeParameter> {
val results = ArrayList<PsiTypeParameter>()
accept(
object : PsiTypeVisitor<Unit>() {
override fun visitArrayType(arrayType: PsiArrayType) {
arrayType.componentType.accept(this)
}
override fun visitClassType(classType: PsiClassType) {
(classType.resolve() as? PsiTypeParameter)?.let { results += it }
classType.parameters.forEach { it.accept(this) }
}
override fun visitWildcardType(wildcardType: PsiWildcardType) {
wildcardType.bound?.accept(this)
}
}
)
return results
}
private fun PsiType.resolveToKotlinType(resolutionFacade: ResolutionFacade): KotlinType? {
val typeParameters = collectTypeParameters()
val components = resolutionFacade.getFrontendService(JavaResolverComponents::class.java)
val rootContext = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY) { null }
val dummyPackageDescriptor = MutablePackageFragmentDescriptor(resolutionFacade.moduleDescriptor, FqName("dummy"))
val dummyClassDescriptor = ClassDescriptorImpl(
dummyPackageDescriptor,
Name.identifier("Dummy"),
Modality.FINAL,
ClassKind.CLASS,
emptyList(),
SourceElement.NO_SOURCE,
false
)
val typeParameterResolver = object : TypeParameterResolver {
override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? {
val psiTypeParameter = (javaTypeParameter as JavaTypeParameterImpl).psi
val index = typeParameters.indexOf(psiTypeParameter)
if (index < 0) return null
return LazyJavaTypeParameterDescriptor(rootContext.child(this), javaTypeParameter, index, dummyClassDescriptor)
}
}
val typeResolver = JavaTypeResolver(rootContext, typeParameterResolver)
val attributes = JavaTypeAttributes(TypeUsage.COMMON)
return typeResolver.transformJavaType(JavaTypeImpl.create(this), attributes).approximateFlexibleTypes(preferNotNull = true)
}
private fun ExpectedTypes.toKotlinTypeInfo(resolutionFacade: ResolutionFacade): TypeInfo {
val candidateTypes = flatMapTo(LinkedHashSet<KotlinType>()) {
val ktType = (it.theType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: return@flatMapTo emptyList()
when (it.theKind) {
ExpectedType.Kind.EXACT, ExpectedType.Kind.SUBTYPE -> listOf(ktType)
ExpectedType.Kind.SUPERTYPE -> listOf(ktType) + ktType.supertypes()
}
}
if (candidateTypes.isEmpty()) {
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
return TypeInfo(nullableAnyType, Variance.INVARIANT)
}
return TypeInfo.ByExplicitCandidateTypes(candidateTypes.toList())
}
override fun createChangeModifierActions(target: JvmModifiersOwner, request: MemberRequest.Modifier): List<IntentionAction> {
val kModifierOwner = target.toKtElement<KtModifierListOwner>() ?: return emptyList()
val modifier = request.modifier
val shouldPresent = request.shouldPresent
val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier)
KtTokens.OPEN_KEYWORD to !shouldPresent
else
javaPsiModifiersMapping[modifier] to shouldPresent
if (kToken == null) return emptyList()
val action = if (shouldPresentMapped)
AddModifierFix.createIfApplicable(kModifierOwner, kToken)
else
RemoveModifierFix(kModifierOwner, kToken, false)
return listOfNotNull(action)
}
override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List<IntentionAction> {
val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList()
if (request.typeParameters.isNotEmpty()) return emptyList()
val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) }
if (!modifierBuilder.isValid) return emptyList()
val resolutionFacade = targetKtClass.getResolutionFacade()
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
val parameterInfos = request.parameters.mapIndexed { index, param ->
val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
val name = param.name ?: "arg${index + 1}"
ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name))
}
val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor()
val constructorInfo = ConstructorInfo(
parameterInfos,
targetKtClass,
isPrimary = needPrimary,
modifierList = modifierBuilder.modifierList,
withBody = true
)
val addConstructorAction = object : CreateCallableFromUsageFix<KtElement>(targetKtClass, listOf(constructorInfo)) {
override fun getFamilyName() = "Add method"
override fun getText() = "Add ${if (needPrimary) "primary" else "secondary"} constructor to '${targetClass.name}'"
}
val changePrimaryConstructorAction = run {
val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null
val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null
val project = targetKtClass.project
val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null
QuickFixFactory.getInstance()
.createChangeMethodSignatureFromUsageFix(
lightMethod,
fakeParametersExpressions,
PsiSubstitutor.EMPTY,
targetKtClass,
false,
2
).takeIf { it.isAvailable(project, null, targetKtClass.containingFile) }
}
return listOfNotNull(changePrimaryConstructorAction, addConstructorAction)
}
override fun createAddPropertyActions(targetClass: JvmClass, request: MemberRequest.Property): List<IntentionAction> {
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifier(request.visibilityModifier) }
if (!modifierBuilder.isValid) return emptyList()
val resolutionFacade = targetContainer.getResolutionFacade()
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
val ktType = (request.propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
val propertyInfo = PropertyInfo(
request.propertyName,
TypeInfo.Empty,
TypeInfo(ktType, Variance.INVARIANT),
request.setterRequired,
listOf(targetContainer),
modifierList = modifierBuilder.modifierList,
withInitializer = true
)
val propertyInfos = if (request.setterRequired) {
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
}
else {
listOf(propertyInfo)
}
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
}
override fun createAddFieldActions(targetClass: JvmClass, request: CreateFieldRequest): List<IntentionAction> {
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
val modifierBuilder = ModifierBuilder(targetContainer, allowJvmStatic = false).apply {
addJvmModifiers(request.modifiers)
addAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME)
}
if (!modifierBuilder.isValid) return emptyList()
val resolutionFacade = targetContainer.getResolutionFacade()
val typeInfo = request.fieldType.toKotlinTypeInfo(resolutionFacade)
val writable = JvmModifier.FINAL !in request.modifiers
val propertyInfo = PropertyInfo(
request.fieldName,
TypeInfo.Empty,
typeInfo,
writable,
listOf(targetContainer),
isForCompanion = JvmModifier.STATIC in request.modifiers,
modifierList = modifierBuilder.modifierList,
withInitializer = true
)
val propertyInfos = if (writable) {
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
}
else {
listOf(propertyInfo)
}
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
}
override fun createAddMethodActions(targetClass: JvmClass, request: CreateMethodRequest): List<IntentionAction> {
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifiers(request.modifiers) }
if (!modifierBuilder.isValid) return emptyList()
val resolutionFacade = targetContainer.getResolutionFacade()
val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade)
val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) ->
ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList())
}
val functionInfo = FunctionInfo(
request.methodName,
TypeInfo.Empty,
returnTypeInfo,
listOf(targetContainer),
parameterInfos,
isAbstract = JvmModifier.ABSTRACT in request.modifiers,
isForCompanion = JvmModifier.STATIC in request.modifiers,
modifierList = modifierBuilder.modifierList,
preferEmptyBody = true
)
val action = object : CreateCallableFromUsageFix<KtElement>(targetContainer, listOf(functionInfo)) {
override fun getFamilyName() = "Add method"
override fun getText() = "Add method '${request.methodName}' to '${targetClass.name}'"
}
return listOf(action)
}
}