From 662cc325f45e3d4f4446a242f5e21689f9eacce7 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Mon, 1 Jun 2015 18:07:52 +0300 Subject: [PATCH] Never invoke intention action when it's not available --- .../inspections/IntentionBasedInspection.kt | 82 ++++++++++++------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/IntentionBasedInspection.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/IntentionBasedInspection.kt index 0f2cb993a8b..8c51fd1b307 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/IntentionBasedInspection.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/IntentionBasedInspection.kt @@ -16,7 +16,11 @@ package org.jetbrains.kotlin.idea.inspections -import com.intellij.codeInspection.* +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInspection.LocalInspectionToolSession +import com.intellij.codeInspection.LocalQuickFixOnPsiElement +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.EditorFactory import com.intellij.openapi.fileEditor.FileDocumentManager @@ -24,42 +28,24 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiFile import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention import org.jetbrains.kotlin.psi.JetElement -public abstract class IntentionBasedInspection( - protected val intentions: List>, +public abstract class IntentionBasedInspection( + protected val intentions: List>, protected val problemText: String?, - protected val elementType: Class + protected val elementType: Class ) : AbstractKotlinInspection() { - constructor(intention: JetSelfTargetingRangeIntention) : this(listOf(intention), null, intention.elementType) + constructor(intention: JetSelfTargetingRangeIntention) : this(listOf(intention), null, intention.elementType) override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { - class IntentionBasedQuickFix( - val intention: JetSelfTargetingRangeIntention, - val targetElement: T - ): LocalQuickFix { - // store text into variable because intention instance is shared and may change its text later - private val text = intention.getText() - - override fun getFamilyName() = intention.getFamilyName() - - override fun getName() = text - - override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - targetElement.getOrCreateEditor()?.let { editor -> - editor.getCaretModel().moveToOffset(targetElement.getTextOffset()) - intention.applyTo(targetElement, editor) - } - } - } - return object : PsiElementVisitor() { override fun visitElement(element: PsiElement) { if (!elementType.isInstance(element) || element.getTextLength() == 0) return @suppress("UNCHECKED_CAST") - val targetElement = element as T + val targetElement = element as TElement val ranges = intentions .map { @@ -82,13 +68,47 @@ public abstract class IntentionBasedInspection( protected open val problemHighlightType: ProblemHighlightType get() = ProblemHighlightType.GENERIC_ERROR_OR_WARNING - private fun PsiElement.getOrCreateEditor(): Editor? { - val file = getContainingFile()?.getVirtualFile() ?: return null - val document = FileDocumentManager.getInstance().getDocument(file) ?: return null + /* 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 class IntentionBasedQuickFix( + val intention: JetSelfTargetingRangeIntention, + targetElement: TElement + ): LocalQuickFixOnPsiElement(targetElement), IntentionAction { + // store text into variable because intention instance is shared and may change its text later + private val _text = intention.getText() - val editorFactory = EditorFactory.getInstance() + override fun getFamilyName() = intention.getFamilyName() - val editors = editorFactory.getEditors(document) - return if (editors.isEmpty()) editorFactory.createEditor(document) else editors[0] + override fun getText(): String = _text + + override fun startInWriteAction() = true + + override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?) = isAvailable() + + override fun isAvailable(project: Project, file: PsiFile, startElement: PsiElement, endElement: PsiElement): Boolean { + assert(startElement == endElement) + return intention.applicabilityRange(startElement as TElement) != null + } + + override fun invoke(project: Project, editor: Editor?, file: PsiFile?) { + applyFix() + } + + override fun invoke(project: Project, file: PsiFile, startElement: PsiElement, endElement: PsiElement) { + assert(startElement == endElement) + startElement.getOrCreateEditor()?.let { editor -> + editor.getCaretModel().moveToOffset(startElement.getTextOffset()) + intention.applyTo(startElement as TElement, editor) + } + } + + private fun PsiElement.getOrCreateEditor(): Editor? { + val file = getContainingFile()?.getVirtualFile() ?: return null + val document = FileDocumentManager.getInstance().getDocument(file) ?: return null + + val editorFactory = EditorFactory.getInstance() + + val editors = editorFactory.getEditors(document) + return if (editors.isEmpty()) editorFactory.createEditor(document) else editors[0] + } } }