FIR IDE: add AddModifierFix
This commit is contained in:
committed by
Ilya Kirillov
parent
5793f77ece
commit
652207dcac
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.quickfix
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
|
||||
open class AddModifierFix(
|
||||
element: KtModifierListOwner,
|
||||
protected val modifier: KtModifierKeywordToken
|
||||
) : KotlinCrossLanguageQuickFixAction<KtModifierListOwner>(element), KotlinUniversalQuickFix {
|
||||
override fun getText(): String {
|
||||
val element = element ?: return ""
|
||||
if (modifier in modalityModifiers || modifier in KtTokens.VISIBILITY_MODIFIERS || modifier == KtTokens.CONST_KEYWORD) {
|
||||
return KotlinBundle.message("fix.add.modifier.text", RemoveModifierFix.getElementName(element), modifier.value)
|
||||
}
|
||||
return KotlinBundle.message("fix.add.modifier.text.generic", modifier.value)
|
||||
}
|
||||
|
||||
override fun getFamilyName() = KotlinBundle.message("fix.add.modifier.family")
|
||||
|
||||
protected fun invokeOnElement(element: KtModifierListOwner?) {
|
||||
element?.addModifier(modifier)
|
||||
|
||||
if (modifier == KtTokens.ABSTRACT_KEYWORD && (element is KtProperty || element is KtNamedFunction)) {
|
||||
element.containingClass()?.run {
|
||||
if (!hasModifier(KtTokens.ABSTRACT_KEYWORD) && !hasModifier(KtTokens.SEALED_KEYWORD)) {
|
||||
addModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun invokeImpl(project: Project, editor: Editor?, file: PsiFile) {
|
||||
val originalElement = element
|
||||
invokeOnElement(originalElement)
|
||||
}
|
||||
|
||||
// TODO: consider checking if this fix is available by testing if the [element] can be refactored by calling
|
||||
// FIR version of [org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtilKt#canRefactor]
|
||||
override fun isAvailableImpl(project: Project, editor: Editor?, file: PsiFile): Boolean = element != null
|
||||
|
||||
interface Factory<T> {
|
||||
fun createFactory(modifier: KtModifierKeywordToken): QuickFixesPsiBasedFactory<PsiElement> {
|
||||
return createFactory(modifier, KtModifierListOwner::class.java)
|
||||
}
|
||||
|
||||
fun <T : KtModifierListOwner> createFactory(
|
||||
modifier: KtModifierKeywordToken,
|
||||
modifierOwnerClass: Class<T>
|
||||
): QuickFixesPsiBasedFactory<PsiElement> {
|
||||
return quickFixesPsiBasedFactory { e ->
|
||||
val modifierListOwner =
|
||||
PsiTreeUtil.getParentOfType(e, modifierOwnerClass, false) ?: return@quickFixesPsiBasedFactory emptyList()
|
||||
listOfNotNull(createIfApplicable(modifierListOwner, modifier))
|
||||
}
|
||||
}
|
||||
|
||||
fun createIfApplicable(modifierListOwner: KtModifierListOwner, modifier: KtModifierKeywordToken): AddModifierFix? {
|
||||
when (modifier) {
|
||||
KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD -> {
|
||||
if (modifierListOwner is KtObjectDeclaration) return null
|
||||
if (modifierListOwner is KtEnumEntry) return null
|
||||
if (modifierListOwner is KtDeclaration && modifierListOwner !is KtClass) {
|
||||
val parentClassOrObject = modifierListOwner.containingClassOrObject ?: return null
|
||||
if (parentClassOrObject is KtObjectDeclaration) return null
|
||||
if (parentClassOrObject is KtEnumEntry) return null
|
||||
}
|
||||
if (modifier == KtTokens.ABSTRACT_KEYWORD
|
||||
&& modifierListOwner is KtClass
|
||||
&& modifierListOwner.hasModifier(KtTokens.INLINE_KEYWORD)
|
||||
) return null
|
||||
}
|
||||
KtTokens.INNER_KEYWORD -> {
|
||||
if (modifierListOwner is KtObjectDeclaration) return null
|
||||
if (modifierListOwner is KtClass) {
|
||||
if (modifierListOwner.isInterface() ||
|
||||
modifierListOwner.isSealed() ||
|
||||
modifierListOwner.isEnum() ||
|
||||
modifierListOwner.isData() ||
|
||||
modifierListOwner.isAnnotation()
|
||||
) return null
|
||||
}
|
||||
}
|
||||
}
|
||||
return AddModifierFix(modifierListOwner, modifier)
|
||||
}
|
||||
|
||||
fun createModifierFix(
|
||||
element: KtModifierListOwner,
|
||||
modifier: KtModifierKeywordToken
|
||||
): T
|
||||
}
|
||||
|
||||
companion object : Factory<AddModifierFix> {
|
||||
private val modalityModifiers = setOf(KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD, KtTokens.FINAL_KEYWORD)
|
||||
override fun createModifierFix(element: KtModifierListOwner, modifier: KtModifierKeywordToken): AddModifierFix =
|
||||
AddModifierFix(element, modifier)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -25,48 +25,26 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
|
||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.util.runOnExpectAndAllActuals
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
/** Similar to [AddModifierFix] but with multi-platform support. */
|
||||
open class AddModifierFixMpp(
|
||||
element: KtModifierListOwner,
|
||||
protected val modifier: KtModifierKeywordToken
|
||||
) : KotlinCrossLanguageQuickFixAction<KtModifierListOwner>(element), KotlinUniversalQuickFix {
|
||||
override fun getText(): String {
|
||||
val element = element ?: return ""
|
||||
if (modifier in modalityModifiers || modifier in VISIBILITY_MODIFIERS || modifier == CONST_KEYWORD) {
|
||||
return KotlinBundle.message("fix.add.modifier.text", RemoveModifierFix.getElementName(element), modifier.value)
|
||||
}
|
||||
return KotlinBundle.message("fix.add.modifier.text.generic", modifier.value)
|
||||
}
|
||||
|
||||
override fun getFamilyName() = KotlinBundle.message("fix.add.modifier.family")
|
||||
|
||||
private fun invokeOnElement(element: KtModifierListOwner?) {
|
||||
element?.addModifier(modifier)
|
||||
|
||||
if (modifier == ABSTRACT_KEYWORD && (element is KtProperty || element is KtNamedFunction)) {
|
||||
element.containingClass()?.run {
|
||||
if (!hasModifier(ABSTRACT_KEYWORD) && !hasModifier(SEALED_KEYWORD)) {
|
||||
addModifier(ABSTRACT_KEYWORD)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
modifier: KtModifierKeywordToken
|
||||
) : AddModifierFix(element, modifier) {
|
||||
|
||||
override fun invokeImpl(project: Project, editor: Editor?, file: PsiFile) {
|
||||
val originalElement = element
|
||||
@@ -82,59 +60,12 @@ open class AddModifierFixMpp(
|
||||
return element.canRefactor()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
companion object : Factory<AddModifierFixMpp> {
|
||||
private fun KtModifierKeywordToken.isMultiplatformPersistent(): Boolean =
|
||||
this in MODALITY_MODIFIERS || this == INLINE_KEYWORD
|
||||
|
||||
private val modalityModifiers = setOf(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD)
|
||||
|
||||
fun createFactory(modifier: KtModifierKeywordToken): KotlinSingleIntentionActionFactory {
|
||||
return createFactory(modifier, KtModifierListOwner::class.java)
|
||||
}
|
||||
|
||||
fun <T : KtModifierListOwner> createFactory(
|
||||
modifier: KtModifierKeywordToken,
|
||||
modifierOwnerClass: Class<T>
|
||||
): KotlinSingleIntentionActionFactory {
|
||||
return object : KotlinSingleIntentionActionFactory() {
|
||||
public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||
val modifierListOwner = QuickFixUtil.getParentElementOfType(diagnostic, modifierOwnerClass) ?: return null
|
||||
return createIfApplicable(modifierListOwner, modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createIfApplicable(modifierListOwner: KtModifierListOwner, modifier: KtModifierKeywordToken): AddModifierFixMpp? {
|
||||
when (modifier) {
|
||||
ABSTRACT_KEYWORD, OPEN_KEYWORD -> {
|
||||
if (modifierListOwner is KtObjectDeclaration) return null
|
||||
if (modifierListOwner is KtEnumEntry) return null
|
||||
if (modifierListOwner is KtDeclaration && modifierListOwner !is KtClass) {
|
||||
val parentClassOrObject = modifierListOwner.containingClassOrObject ?: return null
|
||||
if (parentClassOrObject is KtObjectDeclaration) return null
|
||||
if (parentClassOrObject is KtEnumEntry) return null
|
||||
}
|
||||
if (modifier == ABSTRACT_KEYWORD
|
||||
&& modifierListOwner is KtClass
|
||||
&& modifierListOwner.hasModifier(INLINE_KEYWORD)
|
||||
) return null
|
||||
}
|
||||
INNER_KEYWORD -> {
|
||||
if (modifierListOwner is KtObjectDeclaration) return null
|
||||
if (modifierListOwner is KtClass) {
|
||||
if (modifierListOwner.isInterface() ||
|
||||
modifierListOwner.isSealed() ||
|
||||
modifierListOwner.isEnum() ||
|
||||
modifierListOwner.isData() ||
|
||||
modifierListOwner.isAnnotation()
|
||||
) return null
|
||||
}
|
||||
}
|
||||
}
|
||||
return AddModifierFixMpp(modifierListOwner, modifier)
|
||||
}
|
||||
|
||||
override fun createModifierFix(element: KtModifierListOwner, modifier: KtModifierKeywordToken): AddModifierFixMpp =
|
||||
AddModifierFixMpp(element, modifier)
|
||||
}
|
||||
|
||||
object MakeClassOpenFactory : KotlinSingleIntentionActionFactory() {
|
||||
|
||||
Reference in New Issue
Block a user