Refactoring: SelfTargetingIntention / IntentionBasedInspection

Remove complex logic which disables intention when bound inspection on
Add much simpler logic in SelfTargetingIntention.equals
This commit is contained in:
Mikhail Glukhikh
2017-05-10 18:22:16 +03:00
parent 1900b20e6e
commit 0361ed8c68
3 changed files with 9 additions and 53 deletions
@@ -147,8 +147,8 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
} }
/* we implement IntentionAction to provide isAvailable which will be used to hide outdated items and make sure we never call 'invoke' for such item */ /* we implement IntentionAction to provide isAvailable which will be used to hide outdated items and make sure we never call 'invoke' for such item */
private open inner class IntentionBasedQuickFix( internal open inner class IntentionBasedQuickFix(
private val intention: SelfTargetingRangeIntention<TElement>, val intention: SelfTargetingRangeIntention<TElement>,
private val additionalChecker: (TElement, IntentionBasedInspection<TElement>) -> Boolean, private val additionalChecker: (TElement, IntentionBasedInspection<TElement>) -> Boolean,
targetElement: TElement targetElement: TElement
) : LocalQuickFixOnPsiElement(targetElement), IntentionAction { ) : LocalQuickFixOnPsiElement(targetElement), IntentionAction {
@@ -17,15 +17,11 @@
package org.jetbrains.kotlin.idea.intentions package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.FileModificationService import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInsight.daemon.HighlightDisplayKey
import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInspection.IntentionWrapper import com.intellij.codeInspection.IntentionWrapper
import com.intellij.codeInspection.LocalInspectionEP
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.profile.codeInspection.InspectionProjectProfileManager
import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
@@ -35,8 +31,6 @@ import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.psiUtil.containsInside import org.jetbrains.kotlin.psi.psiUtil.containsInside
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import java.util.*
import kotlin.reflect.KClass
@Suppress("EqualsOrHashCode") @Suppress("EqualsOrHashCode")
abstract class SelfTargetingIntention<TElement : PsiElement>( abstract class SelfTargetingIntention<TElement : PsiElement>(
@@ -88,25 +82,11 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
protected open fun allowCaretInsideElement(element: PsiElement): Boolean = protected open fun allowCaretInsideElement(element: PsiElement): Boolean =
element !is KtBlockExpression element !is KtBlockExpression
final override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean { final override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = getTarget(editor, file) != null
val target = getTarget(editor, file) ?: return false
return !isIntentionBaseInspectionEnabled(project, target)
}
var inspection: IntentionBasedInspection<TElement>? = null var inspection: IntentionBasedInspection<TElement>? = null
internal set internal set
protected fun isIntentionBaseInspectionEnabled(project: Project, target: TElement): Boolean {
val inspection = inspection ?: findInspection(this::class.java.kotlin) ?: return false
val key = HighlightDisplayKey.find(inspection.shortName)
if (!InspectionProjectProfileManager.getInstance(project).inspectionProfile.isToolEnabled(key, target)) {
return false
}
return inspection.intentionInfos.single { it.intention == this::class.java.kotlin }.additionalChecker(target, inspection)
}
final override fun invoke(project: Project, editor: Editor, file: PsiFile): Unit { final override fun invoke(project: Project, editor: Editor, file: PsiFile): Unit {
PsiDocumentManager.getInstance(project).commitAllDocuments() PsiDocumentManager.getInstance(project).commitAllDocuments()
val target = getTarget(editor, file) ?: return val target = getTarget(editor, file) ?: return
@@ -121,32 +101,11 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
// Nasty code because IntentionWrapper itself does not override equals // Nasty code because IntentionWrapper itself does not override equals
if (other is IntentionWrapper) return this == other.action if (other is IntentionWrapper) return this == other.action
if (other is IntentionBasedInspection<*>.IntentionBasedQuickFix) return this == other.intention
return other is SelfTargetingIntention<*> && javaClass == other.javaClass && text == other.text return other is SelfTargetingIntention<*> && javaClass == other.javaClass && text == other.text
} }
// Intentionally missed hashCode (IntentionWrapper does not override it) // Intentionally missed hashCode (IntentionWrapper does not override it)
companion object {
private val intentionBasedInspections = HashMap<KClass<out SelfTargetingIntention<*>>, IntentionBasedInspection<*>?>()
fun <TElement : PsiElement> findInspection(intentionClass: KClass<out SelfTargetingIntention<TElement>>): IntentionBasedInspection<TElement>? {
if (intentionBasedInspections.containsKey(intentionClass)) {
@Suppress("UNCHECKED_CAST")
return intentionBasedInspections[intentionClass] as IntentionBasedInspection<TElement>?
}
for (extension in Extensions.getExtensions(LocalInspectionEP.LOCAL_INSPECTION)) {
val inspection = extension.instance as? IntentionBasedInspection<*> ?: continue
if (inspection.intentionInfos.any { it.intention == intentionClass }) {
intentionBasedInspections[intentionClass] = inspection
@Suppress("UNCHECKED_CAST")
return inspection as IntentionBasedInspection<TElement>
}
}
return null
}
}
} }
abstract class SelfTargetingRangeIntention<TElement : PsiElement>( abstract class SelfTargetingRangeIntention<TElement : PsiElement>(
@@ -32,15 +32,12 @@ class ChangePackageToMatchDirectoryIntention : SelfTargetingOffsetIndependentInt
if (file.isInjectedFragment || file.packageMatchesDirectory()) return false if (file.isInjectedFragment || file.packageMatchesDirectory()) return false
val fqNameByDirectory = file.getFqNameByDirectory() val fqNameByDirectory = file.getFqNameByDirectory()
if (!fqNameByDirectory.hasIdentifiersOnly()) { text = if (!fqNameByDirectory.hasIdentifiersOnly()) {
if (isIntentionBaseInspectionEnabled(file.project, element)) { "File package doesn't match directory (cannot fix)"
text = "File package doesn't match directory"
return true
} }
return false else {
"Change file's package to '${fqNameByDirectory.asString()}'"
} }
text = "Change file's package to '${fqNameByDirectory.asString()}'"
return true return true
} }