FIR IDE: Implement applicatorByQuickFix() to re-use quick fix actions

from idea-frontend-independent.
This commit is contained in:
Mark Punzalan
2021-03-09 23:08:41 +00:00
committed by Ilya Kirillov
parent 1b649fa4cb
commit 3ebdfa3850
3 changed files with 64 additions and 10 deletions
@@ -10,6 +10,7 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.PrivateForInline
import org.jetbrains.kotlin.idea.frontend.api.ForbidKtResolve
import org.jetbrains.kotlin.idea.quickfix.KotlinPsiOnlyQuickFixAction
import kotlin.experimental.ExperimentalTypeInference
import kotlin.reflect.KClass
@@ -194,3 +195,19 @@ fun <PSI : PsiElement, INPUT : HLApplicatorInput> applicator(
init: HLApplicatorBuilder<PSI, INPUT>.() -> Unit,
): HLApplicator<PSI, INPUT> =
HLApplicatorBuilder<PSI, INPUT>().apply(init).build()
/**
* Builds a new applicator that uses a [KotlinPsiOnlyQuickFixAction] to apply the fix to the PSI
*
* @see HLApplicator
*/
fun <PSI : PsiElement, INPUT : HLApplicatorInput, QUICK_FIX : KotlinPsiOnlyQuickFixAction<PSI>> applicatorByQuickFix(
getFamilyName: () -> String,
isApplicableByPsi: (PSI) -> Boolean = { true },
quickFixByInput: (PSI, INPUT) -> QUICK_FIX,
): HLApplicator<PSI, INPUT> = HLApplicatorImpl(
applyTo = { psi, input, project, editor -> quickFixByInput(psi, input).invoke(project ?: psi.project, editor, psi.containingFile) },
isApplicableByPsi = isApplicableByPsi,
getActionName = { psi, input -> quickFixByInput(psi, input).text },
getFamilyName = getFamilyName
)
@@ -0,0 +1,19 @@
/*
* 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.fir.api.fixes
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicator
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicatorInput
class HLApplicatorWithTargetAndInput<PSI : PsiElement, INPUT : HLApplicatorInput>(
val applicator: HLApplicator<PSI, INPUT>,
private val targetAndInput: HLApplicatorTargetWithInput<PSI, INPUT>,
) {
operator fun component1() = applicator
operator fun component2() = targetAndInput.target
operator fun component3() = targetAndInput.input
}
@@ -12,17 +12,22 @@ import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicatorInput
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi
sealed class HLDiagnosticFixFactory<DIAGNOSTIC_PSI : PsiElement, in DIAGNOSTIC : KtDiagnosticWithPsi<DIAGNOSTIC_PSI>, TARGET_PSI : PsiElement, INPUT : HLApplicatorInput>(
val applicator: HLApplicator<TARGET_PSI, INPUT>
) {
abstract fun KtAnalysisSession.createTargets(diagnostic: DIAGNOSTIC): List<HLApplicatorTargetWithInput<TARGET_PSI, INPUT>>
sealed class HLDiagnosticFixFactory<DIAGNOSTIC_PSI : PsiElement, in DIAGNOSTIC : KtDiagnosticWithPsi<DIAGNOSTIC_PSI>, TARGET_PSI : PsiElement, INPUT : HLApplicatorInput> {
abstract fun KtAnalysisSession.createTargets(diagnostic: DIAGNOSTIC): List<HLApplicatorWithTargetAndInput<TARGET_PSI, INPUT>>
}
private class HLDiagnosticFixFactoryImpl<DIAGNOSTIC_PSI : PsiElement, DIAGNOSTIC : KtDiagnosticWithPsi<DIAGNOSTIC_PSI>, TARGET_PSI : PsiElement, INPUT : HLApplicatorInput>(
applicator: HLApplicator<TARGET_PSI, INPUT>,
private class HLDiagnosticFixFactoryWithFixedApplicator<DIAGNOSTIC_PSI : PsiElement, DIAGNOSTIC : KtDiagnosticWithPsi<DIAGNOSTIC_PSI>, TARGET_PSI : PsiElement, INPUT : HLApplicatorInput>(
private val applicator: HLApplicator<TARGET_PSI, INPUT>,
private val createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<HLApplicatorTargetWithInput<TARGET_PSI, INPUT>>
) : HLDiagnosticFixFactory<DIAGNOSTIC_PSI, DIAGNOSTIC, TARGET_PSI, INPUT>(applicator) {
override fun KtAnalysisSession.createTargets(diagnostic: DIAGNOSTIC): List<HLApplicatorTargetWithInput<TARGET_PSI, INPUT>> =
) : HLDiagnosticFixFactory<DIAGNOSTIC_PSI, DIAGNOSTIC, TARGET_PSI, INPUT>() {
override fun KtAnalysisSession.createTargets(diagnostic: DIAGNOSTIC): List<HLApplicatorWithTargetAndInput<TARGET_PSI, INPUT>> =
createQuickFixes.invoke(this, diagnostic).map { targetAndInput -> HLApplicatorWithTargetAndInput(applicator, targetAndInput) }
}
private class HLDiagnosticFixFactoryWithVariableApplicator<DIAGNOSTIC_PSI : PsiElement, DIAGNOSTIC : KtDiagnosticWithPsi<DIAGNOSTIC_PSI>, TARGET_PSI : PsiElement, INPUT : HLApplicatorInput>(
private val createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<HLApplicatorWithTargetAndInput<TARGET_PSI, INPUT>>
) : HLDiagnosticFixFactory<DIAGNOSTIC_PSI, DIAGNOSTIC, TARGET_PSI, INPUT>() {
override fun KtAnalysisSession.createTargets(diagnostic: DIAGNOSTIC): List<HLApplicatorWithTargetAndInput<TARGET_PSI, INPUT>> =
createQuickFixes.invoke(this, diagnostic)
}
@@ -30,11 +35,24 @@ internal fun <DIAGNOSTIC : KtDiagnosticWithPsi<PsiElement>> KtAnalysisSession.cr
diagnostic: DIAGNOSTIC,
factory: HLDiagnosticFixFactory<PsiElement, DIAGNOSTIC, PsiElement, HLApplicatorInput>
): List<IntentionAction> = with(factory) {
createTargets(diagnostic).map { (target, input) -> HLQuickFix(target, input, factory.applicator) }
createTargets(diagnostic).map { (applicator, target, input) -> HLQuickFix(target, input, applicator) }
}
/**
* Returns a [HLDiagnosticFixFactory] that creates targets and inputs ([HLApplicatorTargetWithInput]) from a diagnostic.
* The targets and inputs are consumed by the given applicator to apply fixes.
*/
fun <DIAGNOSTIC_PSI : PsiElement, DIAGNOSTIC : KtDiagnosticWithPsi<DIAGNOSTIC_PSI>, TARGET_PSI : PsiElement, INPUT : HLApplicatorInput> diagnosticFixFactory(
applicator: HLApplicator<TARGET_PSI, INPUT>,
createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<HLApplicatorTargetWithInput<TARGET_PSI, INPUT>>
): HLDiagnosticFixFactory<DIAGNOSTIC_PSI, DIAGNOSTIC, TARGET_PSI, INPUT> =
HLDiagnosticFixFactoryImpl(applicator, createQuickFixes)
HLDiagnosticFixFactoryWithFixedApplicator(applicator, createQuickFixes)
/**
* Returns a [HLDiagnosticFixFactory] that creates applicators, targets, and inputs ([HLApplicatorWithTargetAndInput]) from a diagnostic.
* The targets and inputs are consumed by the corresponding applicator to apply fixes.
*/
fun <DIAGNOSTIC_PSI : PsiElement, DIAGNOSTIC : KtDiagnosticWithPsi<DIAGNOSTIC_PSI>, TARGET_PSI : PsiElement, INPUT : HLApplicatorInput> diagnosticFixFactory(
createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<HLApplicatorWithTargetAndInput<TARGET_PSI, INPUT>>
): HLDiagnosticFixFactory<DIAGNOSTIC_PSI, DIAGNOSTIC, TARGET_PSI, INPUT> =
HLDiagnosticFixFactoryWithVariableApplicator(createQuickFixes)