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 2aa8f95d83a..e15e3c60241 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 @@ -23,15 +23,14 @@ import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor -import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention +import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention import org.jetbrains.kotlin.psi.JetElement -import java.util.* public abstract class IntentionBasedInspection( - protected val intentions: List>, + protected val intentions: List>, protected val elementType: Class ) : AbstractKotlinInspection() { - constructor(intention: JetSelfTargetingOffsetIndependentIntention): this(listOf(intention), intention.elementType) + constructor(intention: JetSelfTargetingRangeIntention): this(listOf(intention), intention.elementType) override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { return object: PsiElementVisitor() { @@ -42,7 +41,10 @@ public abstract class IntentionBasedInspection( val targetElement = element as T for (intention in intentions) { - if (!intention.isApplicableTo(targetElement)) continue + val range = intention.applicabilityRange(targetElement) ?: continue + val elementRange = targetElement.getTextRange() + assert(range in elementRange, "Wrong applicabilityRange() result for $intention - should be within element's range") + val rangeInElement = range.shiftRight(-elementRange.getStartOffset()) val fix = object: LocalQuickFix { private val text = intention.getText() @@ -59,7 +61,7 @@ public abstract class IntentionBasedInspection( } } - holder.registerProblem(targetElement, intention.getText(), problemHighlightType, fix) + holder.registerProblem(targetElement, intention.getText(), problemHighlightType, rangeInElement, fix) } } } @@ -67,14 +69,14 @@ 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 - - val editorFactory = EditorFactory.getInstance()!! - - val editors = editorFactory.getEditors(document) - return if (editors.isEmpty()) editorFactory.createEditor(document) else editors[0] + + 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] + } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/JetSelfTargetingIntention.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/JetSelfTargetingIntention.kt index 914d3aba7f3..6cff2d0462e 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/JetSelfTargetingIntention.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/JetSelfTargetingIntention.kt @@ -19,12 +19,12 @@ package org.jetbrains.kotlin.idea.intentions import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.idea.JetBundle import org.jetbrains.kotlin.psi.JetElement -import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate import org.jetbrains.kotlin.psi.psiUtil.parents public abstract class JetSelfTargetingIntention( @@ -88,7 +88,7 @@ public abstract class JetSelfTargetingIntention( override fun toString(): String = getText() } -public abstract class JetSelfTargetingOffsetIndependentIntention( +public abstract class JetSelfTargetingRangeIntention( elementType: Class, text: String, familyName: String = text, @@ -99,7 +99,28 @@ public abstract class JetSelfTargetingOffsetIndependentIntention) : this(elementType, JetBundle.message(key), JetBundle.message(key + ".family")) { } + public abstract fun applicabilityRange(element: TElement): TextRange? + + override final fun isApplicableTo(element: TElement, caretOffset: Int): Boolean { + val range = applicabilityRange(element) ?: return false + return range.containsOffset(caretOffset) + } +} + +public abstract class JetSelfTargetingOffsetIndependentIntention( + elementType: Class, + text: String, + familyName: String = text, + firstElementOfTypeOnly: Boolean = false +) : JetSelfTargetingRangeIntention(elementType, text, familyName, firstElementOfTypeOnly) { + + deprecated("Use primary constructor, no need to use i18n") + public constructor(key: String, elementType: Class) : this(elementType, JetBundle.message(key), JetBundle.message(key + ".family")) { + } + public abstract fun isApplicableTo(element: TElement): Boolean - override final fun isApplicableTo(element: TElement, caretOffset: Int): Boolean = isApplicableTo(element) + override final fun applicabilityRange(element: TElement): TextRange? { + return if (isApplicableTo(element)) element.getTextRange() else null + } } diff --git a/idea/resources/inspectionDescriptions/IfNullToElvis.html b/idea/resources/inspectionDescriptions/IfNullToElvis.html new file mode 100644 index 00000000000..630afdfe18e --- /dev/null +++ b/idea/resources/inspectionDescriptions/IfNullToElvis.html @@ -0,0 +1,5 @@ + + +This inspection reports an if expression checking variable being null right after initializing it that can be converted into an elvis operator in the initializer + + diff --git a/idea/resources/inspectionDescriptions/IntroduceWhenSubject.html b/idea/resources/inspectionDescriptions/IntroduceWhenSubject.html new file mode 100644 index 00000000000..fd25616c3dd --- /dev/null +++ b/idea/resources/inspectionDescriptions/IntroduceWhenSubject.html @@ -0,0 +1,5 @@ + + +This inspection reports any 'when' expression that can be simplified by introducing subject argument + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index aa59c84dd27..ccb7817bb8e 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -924,6 +924,13 @@ level="WEAK WARNING" /> + + (javaClass(), "Introduce argument to 'when'") { - override fun isApplicableTo(element: JetWhenExpression): Boolean { - val subject = element.getSubjectToIntroduce() ?: return false - setText("Introduce '$subject' as argument to 'when'") - return true +public class IntroduceWhenSubjectIntention : JetSelfTargetingRangeIntention(javaClass(), "Introduce argument to 'when'") { + override fun applicabilityRange(element: JetWhenExpression): TextRange? { + val subject = element.getSubjectToIntroduce() ?: return null + setText("Introduce '${subject.getText()}' as argument to 'when'") + return element.getWhenKeywordElement().getTextRange() } override fun applyTo(element: JetWhenExpression, editor: Editor) { diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/inspections.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/inspections.kt index b2abc765ff4..4e4df91286d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/inspections.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/inspections.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection import org.jetbrains.kotlin.idea.intentions.attributeCallReplacements.ReplaceGetIntention import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToElvisIntention import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToSafeAccessIntention +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IntroduceWhenSubjectIntention import org.jetbrains.kotlin.psi.* public class ExplicitGetInspection : IntentionBasedInspection(ReplaceGetIntention()) @@ -40,3 +41,5 @@ public class SimplifyBinaryNegationInspection : IntentionBasedInspection(ReplaceWithOperatorAssignmentIntention()) +public class IntroduceWhenSubjectInspection : IntentionBasedInspection(IntroduceWhenSubjectIntention()) +