FIR IDE: extract common logic idea.core.overrideImplement

Also rename OverrideImplementMemberHandler to GenerateMemberHandler to
align with the naming of its member functions.
This commit is contained in:
Tianyu Geng
2021-04-12 14:15:33 -07:00
committed by TeamCityServer
parent 06ba143afe
commit 34387e228f
24 changed files with 264 additions and 195 deletions
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.core
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
fun <T : KtDeclaration> insertMembersAfterAndReformat(
editor: Editor?,
classOrObject: KtClassOrObject,
members: Collection<T>,
anchor: PsiElement? = null,
getAnchor: (KtDeclaration) -> PsiElement? = { null },
): List<T> {
val codeStyleManager = CodeStyleManager.getInstance(classOrObject.project)
return runWriteAction {
val insertedMembersElementPointers = insertMembersAfter(editor, classOrObject, members, anchor, getAnchor)
val firstElement = insertedMembersElementPointers.firstOrNull() ?: return@runWriteAction emptyList()
fun insertedMembersElements() = insertedMembersElementPointers.mapNotNull { it.element }
ShortenReferences.DEFAULT.process(insertedMembersElements())
if (editor != null) {
firstElement.element?.let { moveCaretIntoGeneratedElement(editor, it) }
}
insertedMembersElementPointers.onEach { it.element?.let { element -> codeStyleManager.reformat(element) } }
insertedMembersElements()
}
}
fun <T : KtDeclaration> insertMember(editor: Editor?, classOrObject: KtClassOrObject, declaration: T, anchor: PsiElement? = null): T {
return insertMembersAfterAndReformat(editor, classOrObject, listOf(declaration), anchor).single()
}
@@ -16,28 +16,18 @@
package org.jetbrains.kotlin.idea.core.overrideImplement
import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInsight.hint.HintManager
import com.intellij.ide.util.MemberChooser
import com.intellij.lang.LanguageCodeInsightActionHandler
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.insertMembersAfter
import org.jetbrains.kotlin.idea.core.insertMembersAfterAndReformat
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.prevSiblingOfSameType
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -45,74 +35,24 @@ import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.util.findCallableMemberBySignature
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandler {
abstract class GenerateMembersHandler : AbstractGenerateMembersHandler<OverrideMemberChooserObject>() {
fun collectMembersToGenerate(classOrObject: KtClassOrObject): Collection<OverrideMemberChooserObject> {
override fun collectMembersToGenerate(classOrObject: KtClassOrObject): Collection<OverrideMemberChooserObject> {
val descriptor = classOrObject.resolveToDescriptorIfAny() ?: return emptySet()
return collectMembersToGenerate(descriptor, classOrObject.project)
}
protected abstract fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject>
private fun showOverrideImplementChooser(
project: Project,
members: Array<OverrideMemberChooserObject>
): MemberChooser<OverrideMemberChooserObject>? {
val chooser = MemberChooser(members, true, true, project)
chooser.title = getChooserTitle()
chooser.show()
if (chooser.exitCode != DialogWrapper.OK_EXIT_CODE) return null
return chooser
override fun generateMembers(
editor: Editor,
classOrObject: KtClassOrObject,
selectedElements: Collection<OverrideMemberChooserObject>,
copyDoc: Boolean
) {
return Companion.generateMembers(editor, classOrObject, selectedElements, copyDoc)
}
protected abstract fun getChooserTitle(): String
protected open fun isValidForClass(classOrObject: KtClassOrObject) = true
override fun isValidFor(editor: Editor, file: PsiFile): Boolean {
if (file !is KtFile) return false
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
val classOrObject = elementAtCaret?.getNonStrictParentOfType<KtClassOrObject>()
return classOrObject != null && isValidForClass(classOrObject)
}
protected abstract fun getNoMembersFoundHint(): String
fun invoke(project: Project, editor: Editor, file: PsiFile, implementAll: Boolean) {
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
val classOrObject = elementAtCaret?.getNonStrictParentOfType<KtClassOrObject>() ?: return
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return
val members = collectMembersToGenerate(classOrObject)
if (members.isEmpty() && !implementAll) {
HintManager.getInstance().showErrorHint(editor, getNoMembersFoundHint())
return
}
val copyDoc: Boolean
val selectedElements: Collection<OverrideMemberChooserObject>
if (implementAll) {
selectedElements = members
copyDoc = false
} else {
val chooser = showOverrideImplementChooser(project, members.toTypedArray()) ?: return
selectedElements = chooser.selectedElements ?: return
copyDoc = chooser.isCopyJavadoc
}
if (selectedElements.isEmpty()) return
PsiDocumentManager.getInstance(project).commitAllDocuments()
generateMembers(editor, classOrObject, selectedElements, copyDoc)
}
override fun invoke(project: Project, editor: Editor, file: PsiFile) {
invoke(project, editor, file, implementAll = ApplicationManager.getApplication().isUnitTestMode)
}
override fun startInWriteAction(): Boolean = false
companion object {
fun generateMembers(
editor: Editor?,
@@ -124,7 +64,7 @@ abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandle
val classBody = classOrObject.body
if (classBody == null) {
insertMembersAfter(editor, classOrObject, selectedMemberDescriptors.keys)
insertMembersAfterAndReformat(editor, classOrObject, selectedMemberDescriptors.keys)
return
}
val offset = editor?.caretModel?.offset ?: classBody.startOffset
@@ -132,7 +72,7 @@ abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandle
it.parent == classBody
}
if (offsetCursorElement != null && offsetCursorElement != classBody.rBrace) {
insertMembersAfter(editor, classOrObject, selectedMemberDescriptors.keys)
insertMembersAfterAndReformat(editor, classOrObject, selectedMemberDescriptors.keys)
return
}
val classLeftBrace = classBody.lBrace
@@ -173,7 +113,7 @@ abstract class OverrideImplementMembersHandler : LanguageCodeInsightActionHandle
return lastElement
}
insertMembersAfter(editor, classOrObject, selectedMemberDescriptors.keys) { getAnchor(it) }
insertMembersAfterAndReformat(editor, classOrObject, selectedMemberDescriptors.keys) { getAnchor(it) }
}
}
}
@@ -22,8 +22,8 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle
import org.jetbrains.kotlin.idea.util.expectedDescriptors
import org.jetbrains.kotlin.js.descriptorUtils.hasPrimaryConstructor
import org.jetbrains.kotlin.psi.KtClass
@@ -31,24 +31,24 @@ import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.resolve.OverrideResolver
open class ImplementMembersHandler : OverrideImplementMembersHandler(), IntentionAction {
open class ImplementMembersHandler : GenerateMembersHandler(), IntentionAction {
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
return OverrideResolver.getMissingImplementations(descriptor)
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.FROM_TEMPLATE) }
.map { OverrideMemberChooserObject.create(project, it, it, BodyType.FROM_TEMPLATE) }
}
override fun getChooserTitle() = KotlinIdeaCoreBundle.message("implement.members.handler.title")
override fun getChooserTitle() = KotlinBundle.message("implement.members.handler.title")
override fun getNoMembersFoundHint() = KotlinIdeaCoreBundle.message("implement.members.handler.no.members.hint")
override fun getNoMembersFoundHint() = KotlinBundle.message("implement.members.handler.no.members.hint")
override fun getText() = familyName
override fun getFamilyName() = KotlinIdeaCoreBundle.message("implement.members.handler.family")
override fun getFamilyName() = KotlinBundle.message("implement.members.handler.family")
override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = isValidFor(editor, file)
}
class ImplementAsConstructorParameter : ImplementMembersHandler() {
override fun getText() = KotlinIdeaCoreBundle.message("action.text.implement.as.constructor.parameters")
override fun getText() = KotlinBundle.message("action.text.implement.as.constructor.parameters")
override fun isValidForClass(classOrObject: KtClassOrObject): Boolean {
if (classOrObject !is KtClass || classOrObject is KtEnumEntry || classOrObject.isInterface()) return false
@@ -67,6 +67,6 @@ class ImplementAsConstructorParameter : ImplementMembersHandler() {
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
return OverrideResolver.getMissingImplementations(descriptor)
.filterIsInstance<PropertyDescriptor>()
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.FROM_TEMPLATE, true) }
.map { OverrideMemberChooserObject.create(project, it, it, BodyType.FROM_TEMPLATE, true) }
}
}
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.TemplateKind
import org.jetbrains.kotlin.idea.core.getFunctionBodyTextFromTemplate
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.*
import org.jetbrains.kotlin.idea.core.overrideImplement.BodyType.*
import org.jetbrains.kotlin.idea.core.util.DescriptorMemberChooserObject
import org.jetbrains.kotlin.idea.j2k.IdeaDocCommentConverter
import org.jetbrains.kotlin.idea.kdoc.KDocElementFactory
@@ -40,15 +40,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden
import org.jetbrains.kotlin.util.findCallableMemberBySignature
interface OverrideMemberChooserObject : ClassMember {
sealed class BodyType(val requiresReturn: Boolean = true) {
object NO_BODY : BodyType()
object EMPTY_OR_TEMPLATE : BodyType(requiresReturn = false)
object FROM_TEMPLATE : BodyType(requiresReturn = false)
object SUPER : BodyType()
object QUALIFIED_SUPER : BodyType()
class Delegate(val receiverName: String) : BodyType()
}
val descriptor: CallableMemberDescriptor
val immediateSuper: CallableMemberDescriptor
@@ -111,12 +102,6 @@ interface OverrideMemberChooserObject : ClassMember {
}
}
enum class MemberGenerateMode {
OVERRIDE,
ACTUAL,
EXPECT
}
fun OverrideMemberChooserObject.generateMember(
targetClass: KtClassOrObject,
copyDoc: Boolean
@@ -271,7 +256,7 @@ private fun generateProperty(
project: Project,
descriptor: PropertyDescriptor,
renderer: DescriptorRenderer,
bodyType: OverrideMemberChooserObject.BodyType,
bodyType: BodyType,
forceOverride: Boolean
): KtProperty {
val newDescriptor = descriptor.wrap(forceOverride)
@@ -308,7 +293,7 @@ private fun generateFunction(
project: Project,
descriptor: FunctionDescriptor,
renderer: DescriptorRenderer,
bodyType: OverrideMemberChooserObject.BodyType,
bodyType: BodyType,
forceOverride: Boolean
): KtFunction {
val newDescriptor = descriptor.wrap(forceOverride)
@@ -336,13 +321,13 @@ private fun generateFunction(
}
}
private fun OverrideMemberChooserObject.BodyType.effectiveBodyType(canBeEmpty: Boolean): OverrideMemberChooserObject.BodyType =
private fun BodyType.effectiveBodyType(canBeEmpty: Boolean): BodyType =
if (!canBeEmpty && this == EMPTY_OR_TEMPLATE) FROM_TEMPLATE else this
fun generateUnsupportedOrSuperCall(
project: Project,
descriptor: CallableMemberDescriptor,
bodyType: OverrideMemberChooserObject.BodyType,
bodyType: BodyType,
canBeEmpty: Boolean = true
): String {
when (bodyType.effectiveBodyType(canBeEmpty)) {
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import java.util.*
class OverrideMembersHandler(private val preferConstructorParameters: Boolean = false) : OverrideImplementMembersHandler() {
class OverrideMembersHandler(private val preferConstructorParameters: Boolean = false) : GenerateMembersHandler() {
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
val result = ArrayList<OverrideMemberChooserObject>()
for (member in descriptor.unsubstitutedMemberScope.getContributedDescriptors()) {
@@ -56,13 +56,13 @@ class OverrideMembersHandler(private val preferConstructorParameters: Boolean =
val bodyType = when {
descriptor.kind == ClassKind.INTERFACE && realSuper.builtIns.isMemberOfAny(realSuper) ->
OverrideMemberChooserObject.BodyType.NO_BODY
BodyType.NO_BODY
immediateSuperToUse.modality == Modality.ABSTRACT ->
OverrideMemberChooserObject.BodyType.FROM_TEMPLATE
BodyType.FROM_TEMPLATE
realSupersToUse.size == 1 ->
OverrideMemberChooserObject.BodyType.SUPER
BodyType.SUPER
else ->
OverrideMemberChooserObject.BodyType.QUALIFIED_SUPER
BodyType.QUALIFIED_SUPER
}
result.add(
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -9,17 +9,14 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.SmartList
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.utils.ifEmpty
import kotlin.math.min
fun moveCaretIntoGeneratedElement(editor: Editor, element: PsiElement) {
@@ -200,75 +197,55 @@ private fun removeAfterOffset(offset: Int, whiteSpace: PsiWhiteSpace): PsiElemen
return whiteSpace
}
@JvmOverloads
fun <T : KtDeclaration> insertMembersAfter(
editor: Editor?,
classOrObject: KtClassOrObject,
members: Collection<T>,
anchor: PsiElement? = null,
getAnchor: (KtDeclaration) -> PsiElement? = { null }
): List<T> {
getAnchor: (KtDeclaration) -> PsiElement? = { null },
): List<SmartPsiElementPointer<T>> {
members.ifEmpty { return emptyList() }
val project = classOrObject.project
return runWriteAction {
val insertedMembers = SmartList<SmartPsiElementPointer<T>>()
fun insertedMembersElements() = insertedMembers.mapNotNull { it.element }
val insertedMembers = SmartList<SmartPsiElementPointer<T>>()
val (parameters, otherMembers) = members.partition { it is KtParameter }
val (parameters, otherMembers) = members.partition { it is KtParameter }
parameters.mapNotNullTo(insertedMembers) {
if (classOrObject !is KtClass) return@mapNotNullTo null
parameters.mapNotNullTo(insertedMembers) {
if (classOrObject !is KtClass) return@mapNotNullTo null
@Suppress("UNCHECKED_CAST")
SmartPointerManager.createPointer(
classOrObject.createPrimaryConstructorParameterListIfAbsent().addParameter(it as KtParameter) as T
)
}
if (otherMembers.isNotEmpty()) {
val body = classOrObject.getOrCreateBody()
var afterAnchor = anchor ?: findInsertAfterAnchor(editor, body) ?: return emptyList()
otherMembers.mapTo(insertedMembers) {
afterAnchor = getAnchor(it) ?: afterAnchor
if (classOrObject is KtClass && classOrObject.isEnum()) {
val enumEntries = classOrObject.declarations.filterIsInstance<KtEnumEntry>()
val bound = (enumEntries.lastOrNull() ?: classOrObject.allChildren.firstOrNull { element ->
element.node.elementType == KtTokens.SEMICOLON
})
if (it !is KtEnumEntry) {
if (bound != null && afterAnchor.startOffset <= bound.startOffset) {
afterAnchor = bound
}
} else if (bound == null && body.declarations.isNotEmpty()) {
afterAnchor = body.lBrace!!
} else if (bound != null && afterAnchor.startOffset > bound.startOffset) {
afterAnchor = bound.prevSibling!!
}
}
it.removeModifier(KtTokens.EXTERNAL_KEYWORD)
@Suppress("UNCHECKED_CAST")
SmartPointerManager.createPointer(
classOrObject.createPrimaryConstructorParameterListIfAbsent().addParameter(it as KtParameter) as T
)
SmartPointerManager.createPointer((body.addAfter(it, afterAnchor) as T).apply { afterAnchor = this })
}
if (otherMembers.isNotEmpty()) {
val body = classOrObject.getOrCreateBody()
var afterAnchor = anchor ?: findInsertAfterAnchor(editor, body) ?: return@runWriteAction emptyList<T>()
otherMembers.mapTo(insertedMembers) {
afterAnchor = getAnchor(it) ?: afterAnchor
if (classOrObject is KtClass && classOrObject.isEnum()) {
val enumEntries = classOrObject.declarations.filterIsInstance<KtEnumEntry>()
val bound = (enumEntries.lastOrNull() ?: classOrObject.allChildren.firstOrNull { element ->
element.node.elementType == KtTokens.SEMICOLON
})
if (it !is KtEnumEntry) {
if (bound != null && afterAnchor.startOffset <= bound.startOffset) {
afterAnchor = bound
}
} else if (bound == null && body.declarations.isNotEmpty()) {
afterAnchor = body.lBrace!!
} else if (bound != null && afterAnchor.startOffset > bound.startOffset) {
afterAnchor = bound.prevSibling!!
}
}
it.removeModifier(KtTokens.EXTERNAL_KEYWORD)
@Suppress("UNCHECKED_CAST")
SmartPointerManager.createPointer((body.addAfter(it, afterAnchor) as T).apply { afterAnchor = this })
}
}
ShortenReferences.DEFAULT.process(insertedMembersElements())
val firstElement = insertedMembersElements().firstOrNull() ?: return@runWriteAction emptyList()
if (editor != null) {
moveCaretIntoGeneratedElement(editor, firstElement)
}
val codeStyleManager = CodeStyleManager.getInstance(project)
insertedMembersElements().forEach { codeStyleManager.reformat(it) }
insertedMembersElements().toList()
}
}
fun <T : KtDeclaration> insertMember(editor: Editor?, classOrObject: KtClassOrObject, declaration: T, anchor: PsiElement? = null): T {
return insertMembersAfter(editor, classOrObject, listOf(declaration), anchor).single()
return insertedMembers
}
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.core.overrideImplement
import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInsight.generation.ClassMember
import com.intellij.codeInsight.hint.HintManager
import com.intellij.ide.util.MemberChooser
import com.intellij.lang.LanguageCodeInsightActionHandler
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
abstract class AbstractGenerateMembersHandler<T : ClassMember> : LanguageCodeInsightActionHandler {
abstract fun collectMembersToGenerate(classOrObject: KtClassOrObject): Collection<T>
protected abstract fun getChooserTitle(): String
protected abstract fun getNoMembersFoundHint(): String
protected abstract fun generateMembers(
editor: Editor,
classOrObject: KtClassOrObject,
selectedElements: Collection<T>,
copyDoc: Boolean
)
protected open fun isValidForClass(classOrObject: KtClassOrObject) = true
private fun showOverrideImplementChooser(
project: Project,
members: Collection<T>
): MemberChooser<T>? {
@Suppress("UNCHECKED_CAST")
val memberArray = members.toTypedArray<ClassMember>() as Array<T>
val chooser = MemberChooser(memberArray, true, true, project)
chooser.title = getChooserTitle()
chooser.show()
if (chooser.exitCode != DialogWrapper.OK_EXIT_CODE) return null
return chooser
}
override fun isValidFor(editor: Editor, file: PsiFile): Boolean {
if (file !is KtFile) return false
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
val classOrObject = elementAtCaret?.getNonStrictParentOfType<KtClassOrObject>()
return classOrObject != null && isValidForClass(classOrObject)
}
override fun invoke(project: Project, editor: Editor, file: PsiFile) {
invoke(project, editor, file, implementAll = ApplicationManager.getApplication().isUnitTestMode)
}
fun invoke(project: Project, editor: Editor, file: PsiFile, implementAll: Boolean) {
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
val classOrObject = elementAtCaret?.getNonStrictParentOfType<KtClassOrObject>() ?: return
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return
val members = collectMembersToGenerate(classOrObject)
if (members.isEmpty() && !implementAll) {
HintManager.getInstance().showErrorHint(editor, getNoMembersFoundHint())
return
}
val copyDoc: Boolean
val selectedElements: Collection<T
>
if (implementAll) {
selectedElements = members
copyDoc = false
} else {
val chooser = showOverrideImplementChooser(project, members) ?: return
selectedElements = chooser.selectedElements ?: return
copyDoc = chooser.isCopyJavadoc
}
if (selectedElements.isEmpty()) return
PsiDocumentManager.getInstance(project).commitAllDocuments()
generateMembers(editor, classOrObject, selectedElements, copyDoc)
}
override fun startInWriteAction(): Boolean = false
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.core.overrideImplement
sealed class BodyType(val requiresReturn: Boolean = true) {
object NO_BODY : BodyType()
object EMPTY_OR_TEMPLATE : BodyType(requiresReturn = false)
object FROM_TEMPLATE : BodyType(requiresReturn = false)
object SUPER : BodyType()
object QUALIFIED_SUPER : BodyType()
class Delegate(val receiverName: String) : BodyType()
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.core.overrideImplement
enum class MemberGenerateMode {
OVERRIDE,
ACTUAL,
EXPECT
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.insertMembersAfter
import org.jetbrains.kotlin.idea.core.insertMembersAfterAndReformat
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
@@ -326,6 +326,6 @@ class KotlinGenerateEqualsAndHashcodeAction : KotlinGenerateMemberActionBase<Kot
addIfNotNull(generateHashCode(project, info, targetClass))
}
val anchor = with(targetClass.declarations) { lastIsInstanceOrNull<KtNamedFunction>() ?: lastOrNull() }
return insertMembersAfter(editor, targetClass, prototypes, anchor)
return insertMembersAfterAndReformat(editor, targetClass, prototypes, anchor)
}
}
@@ -124,7 +124,7 @@ class KotlinGenerateSecondaryConstructorAction : KotlinGenerateMemberActionBase<
return emptyList()
}
insertMembersAfter(editor, targetClass, prototypes, findAnchor())
insertMembersAfterAndReformat(editor, targetClass, prototypes, findAnchor())
}
}
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.insertMember
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType
import org.jetbrains.kotlin.idea.core.overrideImplement.BodyType
import org.jetbrains.kotlin.idea.core.overrideImplement.generateUnsupportedOrSuperCall
import org.jetbrains.kotlin.idea.j2k.j2k
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.setupEditorSelection
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.insertMembersAfter
import org.jetbrains.kotlin.idea.core.insertMembersAfterAndReformat
import org.jetbrains.kotlin.idea.core.util.DescriptorMemberChooserObject
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.*
@@ -202,6 +202,6 @@ class KotlinGenerateToStringAction : KotlinGenerateMemberActionBase<KotlinGenera
val targetClass = info.classDescriptor.source.getPsi() as KtClass
val prototype = generateToString(targetClass, info) ?: return emptyList()
val anchor = with(targetClass.declarations) { lastIsInstanceOrNull<KtNamedFunction>() ?: lastOrNull() }
return insertMembersAfter(editor, targetClass, listOf(prototype), anchor)
return insertMembersAfterAndReformat(editor, targetClass, listOf(prototype), anchor)
}
}
@@ -11,6 +11,7 @@ import com.intellij.openapi.ui.Messages
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.overrideImplement.BodyType
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
@@ -70,6 +71,6 @@ fun confirmMemberRewrite(targetClass: KtClass, vararg descriptors: FunctionDescr
fun generateFunctionSkeleton(descriptor: FunctionDescriptor, targetClass: KtClassOrObject): KtNamedFunction {
return OverrideMemberChooserObject
.create(targetClass.project, descriptor, descriptor, OverrideMemberChooserObject.BodyType.FROM_TEMPLATE)
.create(targetClass.project, descriptor, descriptor, BodyType.FROM_TEMPLATE)
.generateMember(targetClass, false) as KtNamedFunction
}
@@ -30,7 +30,8 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaClassDescriptor
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
import org.jetbrains.kotlin.idea.core.overrideImplement.BodyType
import org.jetbrains.kotlin.idea.core.overrideImplement.GenerateMembersHandler
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
import org.jetbrains.kotlin.idea.core.util.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.refactoring.isAbstract
@@ -120,10 +121,10 @@ abstract class ImplementAbstractMemberIntentionBase : SelfTargetingRangeIntentio
member.project,
descriptorToImplement,
descriptorToImplement,
OverrideMemberChooserObject.BodyType.FROM_TEMPLATE,
BodyType.FROM_TEMPLATE,
preferConstructorParameters
)
OverrideImplementMembersHandler.generateMembers(editor, targetClass, listOf(chooserObject), false)
GenerateMembersHandler.generateMembers(editor, targetClass, listOf(chooserObject), false)
}
private fun implementInJavaClass(member: KtNamedDeclaration, targetClass: PsiClass) {
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
import org.jetbrains.kotlin.idea.core.overrideImplement.GenerateMembersHandler
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.lexer.KtTokens
@@ -60,7 +60,7 @@ class MakePrivateAndOverrideMemberFix(
if (makePrivate) {
element.addModifier(KtTokens.PRIVATE_KEYWORD)
}
OverrideImplementMembersHandler.generateMembers(editor, containingClassOrObject, listOf(memberToOverride), false)
GenerateMembersHandler.generateMembers(editor, containingClassOrObject, listOf(memberToOverride), false)
}
object AccidentalOverrideFactory : KotlinSingleIntentionActionFactory() {
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.overrideImplement.BodyType
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember
import org.jetbrains.kotlin.lexer.KtTokens
@@ -72,7 +73,7 @@ class SpecifyOverrideExplicitlyFix(
val overrideMemberChooserObject = OverrideMemberChooserObject.create(
project, delegatedDescriptor, overriddenDescriptor,
OverrideMemberChooserObject.BodyType.Delegate(delegateTargetDescriptor.name.asString())
BodyType.Delegate(delegateTargetDescriptor.name.asString())
)
val member = overrideMemberChooserObject.generateMember(element, copyDoc = false)
val insertedMember = element.addDeclaration(member)
@@ -20,8 +20,8 @@ import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.findOrCreateDirectoryForPackage
import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefix
import org.jetbrains.kotlin.idea.core.overrideImplement.MemberGenerateMode
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.EMPTY_OR_TEMPLATE
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.NO_BODY
import org.jetbrains.kotlin.idea.core.overrideImplement.BodyType.EMPTY_OR_TEMPLATE
import org.jetbrains.kotlin.idea.core.overrideImplement.BodyType.NO_BODY
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.Companion.create
import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember
import org.jetbrains.kotlin.idea.core.overrideImplement.makeNotActual
@@ -19,7 +19,7 @@ import com.intellij.util.SmartList
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
import org.jetbrains.kotlin.idea.core.overrideImplement.GenerateMembersHandler
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMembersHandler
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
@@ -88,33 +88,33 @@ abstract class AbstractOverrideImplementTest : KotlinLightCodeInsightFixtureTest
myFixture.checkResultByFile(getTestName(true) + "/foo/JavaClass.java.after")
}
private fun doFileTest(handler: OverrideImplementMembersHandler, memberToOverride: String? = null) {
private fun doFileTest(handler: GenerateMembersHandler, memberToOverride: String? = null) {
myFixture.configureByFile(getTestName(true) + ".kt")
doOverrideImplement(handler, memberToOverride)
checkResultByFile(getTestName(true) + ".kt.after")
}
private fun doMultiFileTest(handler: OverrideImplementMembersHandler) {
private fun doMultiFileTest(handler: GenerateMembersHandler) {
myFixture.configureByFile(getTestName(true) + ".kt")
doMultiOverrideImplement(handler)
checkResultByFile(getTestName(true) + ".kt.after")
}
private fun doDirectoryTest(handler: OverrideImplementMembersHandler, memberToOverride: String? = null) {
private fun doDirectoryTest(handler: GenerateMembersHandler, memberToOverride: String? = null) {
myFixture.copyDirectoryToProject(getTestName(true), "")
myFixture.configureFromTempProjectFile("foo/Impl.kt")
doOverrideImplement(handler, memberToOverride)
checkResultByFile(getTestName(true) + "/foo/Impl.kt.after")
}
private fun doMultiDirectoryTest(handler: OverrideImplementMembersHandler) {
private fun doMultiDirectoryTest(handler: GenerateMembersHandler) {
myFixture.copyDirectoryToProject(getTestName(true), "")
myFixture.configureFromTempProjectFile("foo/Impl.kt")
doMultiOverrideImplement(handler)
checkResultByFile(getTestName(true) + "/foo/Impl.kt.after")
}
private fun doOverrideImplement(handler: OverrideImplementMembersHandler, memberToOverride: String?) {
private fun doOverrideImplement(handler: GenerateMembersHandler, memberToOverride: String?) {
val elementAtCaret = myFixture.file.findElementAt(myFixture.editor.caretModel.offset)
val classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, KtClassOrObject::class.java)
?: error("Caret should be inside class or object")
@@ -138,7 +138,7 @@ abstract class AbstractOverrideImplementTest : KotlinLightCodeInsightFixtureTest
performGenerateCommand(classOrObject, listOf(singleToOverride))
}
private fun doMultiOverrideImplement(handler: OverrideImplementMembersHandler) {
private fun doMultiOverrideImplement(handler: GenerateMembersHandler) {
val elementAtCaret = myFixture.file.findElementAt(myFixture.editor.caretModel.offset)
val classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, KtClassOrObject::class.java)
?: error("Caret should be inside class or object")
@@ -167,7 +167,7 @@ abstract class AbstractOverrideImplementTest : KotlinLightCodeInsightFixtureTest
try {
val copyDoc = InTextDirectivesUtils.isDirectiveDefined(classOrObject.containingFile.text, "// COPY_DOC")
myFixture.project.executeWriteCommand("") {
OverrideImplementMembersHandler.generateMembers(myFixture.editor, classOrObject, selectedElements, copyDoc)
GenerateMembersHandler.generateMembers(myFixture.editor, classOrObject, selectedElements, copyDoc)
}
} catch (throwable: Throwable) {
throw rethrow(throwable)