FIR IDE: introduce KtQuickFixService
This commit is contained in:
+30
-10
@@ -9,10 +9,15 @@ import com.intellij.codeHighlighting.TextEditorHighlightingPass
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactoryRegistrar
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar
|
||||
import com.intellij.codeInsight.daemon.impl.AnnotationHolderImpl
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
|
||||
import com.intellij.codeInsight.daemon.impl.UpdateHighlightersUtil
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.lang.annotation.AnnotationBuilder
|
||||
import com.intellij.lang.annotation.AnnotationSession
|
||||
import com.intellij.lang.annotation.HighlightSeverity
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
@@ -20,27 +25,41 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyze
|
||||
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnostic
|
||||
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi
|
||||
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.getDefaultMessageWithFactoryName
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
|
||||
import org.jetbrains.kotlin.idea.quickfix.KtQuickFixService
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinHighLevelDiagnosticHighlightingPass(
|
||||
private val ktFile: KtFile,
|
||||
document: Document,
|
||||
) : TextEditorHighlightingPass(ktFile.project, document) {
|
||||
private val diagnosticInfos = mutableListOf<HighlightInfo>()
|
||||
|
||||
override fun doCollectInformation(progress: ProgressIndicator) = analyze(ktFile) {
|
||||
ktFile.collectDiagnosticsForFile().forEach { diagnostic ->
|
||||
diagnostic.textRanges.forEach { range ->
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR/*TODO*/)
|
||||
.descriptionAndTooltip(diagnostic.getMessageToRender())
|
||||
.range(range)
|
||||
.create()
|
||||
?.let(diagnosticInfos::add)
|
||||
@Suppress("UnstableApiUsage")
|
||||
val annotationHolder = AnnotationHolderImpl(AnnotationSession(ktFile))
|
||||
|
||||
override fun doCollectInformation(progress: ProgressIndicator) {
|
||||
analyze(ktFile) {
|
||||
ktFile.collectDiagnosticsForFile().forEach { diagnostic ->
|
||||
addDiagnostic(diagnostic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDiagnostic(diagnostic: KtDiagnosticWithPsi) {
|
||||
val fixes = service<KtQuickFixService>().getQuickFixesFor(diagnostic as KtFirDiagnostic)
|
||||
annotationHolder.runAnnotatorWithContext(diagnostic.psi) { element, annotator ->
|
||||
annotationHolder.newAnnotation(HighlightSeverity.ERROR, diagnostic.getMessageToRender())
|
||||
.addFixes(fixes)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun AnnotationBuilder.addFixes(fixes: List<IntentionAction>) =
|
||||
fixes.fold(this, AnnotationBuilder::withFix)
|
||||
|
||||
private fun KtDiagnostic.getMessageToRender(): String =
|
||||
if (isInternalOrUnitTestMode())
|
||||
getDefaultMessageWithFactoryName()
|
||||
@@ -53,6 +72,7 @@ class KotlinHighLevelDiagnosticHighlightingPass(
|
||||
|
||||
|
||||
override fun doApplyInformationToEditor() {
|
||||
val diagnosticInfos = annotationHolder.map(HighlightInfo::fromAnnotation)
|
||||
UpdateHighlightersUtil.setHighlightersToEditor(
|
||||
myProject, myDocument!!, /*startOffset=*/0, ktFile.textLength, diagnosticInfos, colorsScheme, id
|
||||
)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
|
||||
|
||||
class KtQuickFixService {
|
||||
private val list = KtQuickFixesList.createCombined(KtQuickFixRegistrar.allQuickFixesList())
|
||||
|
||||
fun getQuickFixesFor(diagnostic: KtFirDiagnostic): List<IntentionAction> =
|
||||
list.getQuickFixesFor(diagnostic)
|
||||
}
|
||||
|
||||
abstract class KtQuickFixRegistrar {
|
||||
protected abstract val list: KtQuickFixesList
|
||||
|
||||
companion object {
|
||||
private val EP_NAME: ExtensionPointName<KtQuickFixRegistrar> =
|
||||
ExtensionPointName.create("org.jetbrains.kotlin.ktQuickFixRegistrar")
|
||||
|
||||
fun allQuickFixesList() = EP_NAME.extensionList.map { it.list }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.codeInsight.intention.IntentionAction
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.PrivateForInline
|
||||
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@RequiresOptIn
|
||||
annotation class ForKtQuickFixesListBuilder()
|
||||
|
||||
|
||||
class KtQuickFixesListBuilder private constructor() {
|
||||
val quickFixes = mutableMapOf<KClass<out KtDiagnosticWithPsi>, MutableList<QuickFixFactory>>()
|
||||
|
||||
inline fun <reified D : KtDiagnosticWithPsi> register(quickFixFactory: QuickFixFactory) {
|
||||
quickFixes.getOrPut(D::class) { mutableListOf() }.add(quickFixFactory)
|
||||
}
|
||||
|
||||
@OptIn(ForKtQuickFixesListBuilder::class)
|
||||
private fun build() = KtQuickFixesList(quickFixes)
|
||||
|
||||
companion object {
|
||||
fun register(init: KtQuickFixesListBuilder.() -> Unit) = KtQuickFixesListBuilder().apply(init).build()
|
||||
}
|
||||
}
|
||||
|
||||
class KtQuickFixesList @ForKtQuickFixesListBuilder constructor(private val quickFixes: Map<KClass<out KtDiagnosticWithPsi>, List<QuickFixFactory>>) {
|
||||
fun getQuickFixesFor(diagnostic: KtDiagnosticWithPsi): List<IntentionAction> {
|
||||
val factories = quickFixes[diagnostic.diagnosticClass] ?: return emptyList()
|
||||
return factories.mapNotNull { it.createQuickFix(diagnostic) }
|
||||
}
|
||||
|
||||
private fun QuickFixFactory.createQuickFix(
|
||||
diagnostic: KtDiagnosticWithPsi
|
||||
) = when (this) {
|
||||
is QuickFixesPsiBasedFactory -> createQuickFix(diagnostic.psi)
|
||||
else -> error("Unsupported QuickFixFactory $this")
|
||||
}
|
||||
|
||||
companion object {
|
||||
@OptIn(ForKtQuickFixesListBuilder::class)
|
||||
fun createCombined(registrars: List<KtQuickFixesList>): KtQuickFixesList {
|
||||
val allQuickFixes = registrars.map { it.quickFixes }.merge()
|
||||
return KtQuickFixesList(allQuickFixes)
|
||||
}
|
||||
|
||||
fun createCombined(vararg registrars: KtQuickFixesList): KtQuickFixesList {
|
||||
return createCombined(registrars.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <K, V> List<Map<K, List<V>>>.merge(): Map<K, List<V>> {
|
||||
return flatMap { it.entries }
|
||||
.groupingBy { it.key }
|
||||
.aggregate<Map.Entry<K, List<V>>, K, MutableList<V>> { _, accumulator, element, _ ->
|
||||
val list = accumulator ?: mutableListOf()
|
||||
list.addAll(element.value)
|
||||
list
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<idea-plugin>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.ktQuickFixRegistrar"
|
||||
interface="org.jetbrains.kotlin.idea.quickfix.KtQuickFixRegistrar"
|
||||
dynamic="true"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
<ktQuickFixRegistrar implementation="org.jetbrains.kotlin.idea.quickfix.MainKtQuickFixRegistrar"/>
|
||||
</extensions>
|
||||
|
||||
</idea-plugin>
|
||||
@@ -40,6 +40,8 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
|
||||
|
||||
<xi:include href="extensions/ide-frontend-independent.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<xi:include href="extensions.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
|
||||
<!-- CIDR-PLUGIN-EXCLUDE-START -->
|
||||
<!-- <xi:include href="jvm-common.xml" xpointer="xpointer(/idea-plugin/*)"/>-->
|
||||
@@ -267,7 +269,7 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.util.FirPluginOracleService"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.util.FirPluginOracleServiceFirImpl"/>
|
||||
|
||||
|
||||
<applicationService serviceImplementation="org.jetbrains.kotlin.idea.quickfix.KtQuickFixService"/>
|
||||
|
||||
<readWriteAccessDetector implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector" id="kotlin"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user