Use highlight visitor instead of custom general like highlighting pass
#KTIJ-1760 Fixed #KT-45254 Fixed
This commit is contained in:
committed by
Space
parent
5e173c9f83
commit
cb92474af9
+126
-97
@@ -1,64 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.highlighter
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.Divider
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInsight.problems.ProblemImpl
|
||||
import com.intellij.lang.annotation.Annotation
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.lang.annotation.Annotator
|
||||
import com.intellij.lang.annotation.HighlightSeverity
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.problems.Problem
|
||||
import com.intellij.problems.WolfTheProblemSolver
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiRecursiveElementVisitor
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.CommonProcessors
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.asJava.getJvmSignatureDiagnostics
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.RenderingContext
|
||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||
import org.jetbrains.kotlin.idea.quickfix.QuickFixes
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.lang.reflect.*
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
abstract class AbstractKotlinHighlightingPass(file: KtFile, document: Document) :
|
||||
AbstractBindingContextAwareHighlightingPassBase(file, document) {
|
||||
override val annotator: Annotator
|
||||
get() = KotlinAfterAnalysisAnnotator()
|
||||
abstract class AbstractKotlinHighlightVisitor : HighlightVisitor {
|
||||
private var afterAnalysisVisitor: Array<AfterAnalysisHighlightingVisitor>? = null
|
||||
|
||||
private inner class KotlinAfterAnalysisAnnotator : Annotator {
|
||||
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
|
||||
val bindingContext = bindingContext()
|
||||
getAfterAnalysisVisitor(holder, bindingContext).forEach { visitor -> element.accept(visitor) }
|
||||
override fun suitableForFile(file: PsiFile) = file is KtFile
|
||||
|
||||
override fun visit(element: PsiElement) {
|
||||
afterAnalysisVisitor?.forEach(element::accept)
|
||||
}
|
||||
|
||||
override fun analyze(psiFile: PsiFile, updateWholeFile: Boolean, holder: HighlightInfoHolder, action: Runnable): Boolean {
|
||||
val file = psiFile as? KtFile ?: return false
|
||||
|
||||
try {
|
||||
analyze(file, holder)
|
||||
|
||||
action.run()
|
||||
} finally {
|
||||
afterAnalysisVisitor = null
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun doApplyInformationToEditor() {
|
||||
super.doApplyInformationToEditor()
|
||||
|
||||
reportErrorsToWolf()
|
||||
}
|
||||
|
||||
override fun buildBindingContext(holder: AnnotationHolder): BindingContext {
|
||||
private fun analyze(file: KtFile, holder: HighlightInfoHolder) {
|
||||
val dividedElements: List<Divider.DividedElements> = ArrayList()
|
||||
Divider.divideInsideAndOutsideAllRoots(
|
||||
file, file.textRange, file.textRange, { true },
|
||||
@@ -71,8 +73,8 @@ abstract class AbstractKotlinHighlightingPass(file: KtFile, document: Document)
|
||||
|
||||
// annotate diagnostics on fly: show diagnostics as soon as front-end reports them
|
||||
// don't create quick fixes as it could require some resolve
|
||||
val annotationByDiagnostic = mutableMapOf<Diagnostic, Annotation>()
|
||||
val annotationByTextRange = mutableMapOf<TextRange, Annotation>()
|
||||
val highlightInfoByDiagnostic = mutableMapOf<Diagnostic, HighlightInfo>()
|
||||
val highlightInfoByTextRange = mutableMapOf<TextRange, HighlightInfo>()
|
||||
|
||||
// render of on-fly diagnostics with descriptors could lead to recursion
|
||||
fun checkIfDescriptor(candidate: Any?): Boolean =
|
||||
@@ -82,61 +84,84 @@ abstract class AbstractKotlinHighlightingPass(file: KtFile, document: Document)
|
||||
file.analyzeWithAllCompilerChecks({
|
||||
val element = it.psiElement
|
||||
if (element in elements &&
|
||||
it !in annotationByDiagnostic &&
|
||||
it !in highlightInfoByDiagnostic &&
|
||||
!RenderingContext.parameters(it).any(::checkIfDescriptor)
|
||||
) {
|
||||
annotateDiagnostic(element, holder, it, annotationByDiagnostic, annotationByTextRange)
|
||||
annotateDiagnostic(
|
||||
file,
|
||||
element,
|
||||
holder,
|
||||
it,
|
||||
highlightInfoByDiagnostic,
|
||||
highlightInfoByTextRange
|
||||
)
|
||||
}
|
||||
}).also { it.throwIfError() }
|
||||
}
|
||||
).also { it.throwIfError() }
|
||||
// resolve is done!
|
||||
|
||||
val bindingContext = analysisResult.bindingContext
|
||||
|
||||
cleanUpCalculatingAnnotations(annotationByTextRange)
|
||||
// TODO: for some reasons it could be duplicated diagnostics for the same factory
|
||||
// see [PsiCheckerTestGenerated$Checker.testRedeclaration]
|
||||
val diagnostics = bindingContext.diagnostics.asSequence().filter { it.psiElement in elements }.toSet()
|
||||
afterAnalysisVisitor = getAfterAnalysisVisitor(holder, bindingContext)
|
||||
|
||||
if (diagnostics.isNotEmpty()) {
|
||||
// annotate diagnostics those were not possible to render on fly
|
||||
diagnostics.asSequence().filterNot { it in annotationByDiagnostic }.forEach {
|
||||
annotateDiagnostic(it.psiElement, holder, it, annotationByDiagnostic, calculatingInProgress = false)
|
||||
}
|
||||
// apply quick fixes for all diagnostics grouping by element
|
||||
diagnostics.groupBy(Diagnostic::psiElement).forEach {
|
||||
annotateQuickFixes(it.key, it.value, annotationByDiagnostic)
|
||||
}
|
||||
cleanUpCalculatingAnnotations(highlightInfoByTextRange)
|
||||
|
||||
annotateDuplicateJvmSignature(file, holder, bindingContext.diagnostics)
|
||||
|
||||
for (diagnostic in bindingContext.diagnostics) {
|
||||
val psiElement = diagnostic.psiElement
|
||||
if (psiElement !in elements) continue
|
||||
// has been processed earlier e.g. on-fly or for some reasons it could be duplicated diagnostics for the same factory
|
||||
// see [PsiCheckerTestGenerated$Checker.testRedeclaration]
|
||||
if (diagnostic in highlightInfoByDiagnostic) continue
|
||||
|
||||
// annotate diagnostics those were not possible to report (and therefore render) on fly
|
||||
annotateDiagnostic(file, psiElement, holder, diagnostic, highlightInfoByDiagnostic, calculatingInProgress = false)
|
||||
}
|
||||
return bindingContext
|
||||
|
||||
// apply quick fixes for all diagnostics grouping by element
|
||||
highlightInfoByDiagnostic.keys.groupBy { it.psiElement }.forEach {
|
||||
annotateQuickFixes(file, it.key, it.value, highlightInfoByDiagnostic)
|
||||
}
|
||||
}
|
||||
|
||||
private fun annotateDuplicateJvmSignature(file: KtFile, holder: HighlightInfoHolder, otherDiagnostics: Diagnostics) {
|
||||
if (!(ProjectRootsUtil.isInProjectSource(file) && TargetPlatformDetector.getPlatform(file).isJvm())) return
|
||||
|
||||
val duplicateJvmSignatureAnnotator =
|
||||
DuplicateJvmSignatureAnnotator(file, holder, otherDiagnostics, file.getModuleInfo().contentScope())
|
||||
duplicateJvmSignatureAnnotator.visit()
|
||||
}
|
||||
|
||||
private fun annotateDiagnostic(
|
||||
file: KtFile,
|
||||
element: PsiElement,
|
||||
holder: AnnotationHolder,
|
||||
holder: HighlightInfoHolder,
|
||||
diagnostic: Diagnostic,
|
||||
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>? = null,
|
||||
annotationByTextRange: MutableMap<TextRange, Annotation>? = null,
|
||||
highlightInfoByDiagnostic: MutableMap<Diagnostic, HighlightInfo>? = null,
|
||||
highlightInfoByTextRange: MutableMap<TextRange, HighlightInfo>? = null,
|
||||
calculatingInProgress: Boolean = true
|
||||
) = annotateDiagnostics(element, holder, listOf(diagnostic), annotationByDiagnostic, annotationByTextRange, true, calculatingInProgress)
|
||||
) = annotateDiagnostics(file, element, holder, listOf(diagnostic), highlightInfoByDiagnostic, highlightInfoByTextRange, true, calculatingInProgress)
|
||||
|
||||
private fun cleanUpCalculatingAnnotations(annotationByTextRange: Map<TextRange, Annotation>) {
|
||||
annotationByTextRange.values.forEach { annotation ->
|
||||
annotation.quickFixes?.removeIf {
|
||||
it.quickFix is CalculatingIntentionAction
|
||||
private fun cleanUpCalculatingAnnotations(highlightInfoByTextRange: Map<TextRange, HighlightInfo>) {
|
||||
highlightInfoByTextRange.values.forEach { annotation ->
|
||||
annotation.unregisterQuickFix {
|
||||
it is CalculatingIntentionAction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun annotateDiagnostics(
|
||||
file: KtFile,
|
||||
element: PsiElement,
|
||||
holder: AnnotationHolder,
|
||||
holder: HighlightInfoHolder,
|
||||
diagnostics: List<Diagnostic>,
|
||||
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>? = null,
|
||||
annotationByTextRange: MutableMap<TextRange, Annotation>? = null,
|
||||
highlightInfoByDiagnostic: MutableMap<Diagnostic, HighlightInfo>? = null,
|
||||
highlightInfoByTextRange: MutableMap<TextRange, HighlightInfo>? = null,
|
||||
noFixes: Boolean = false,
|
||||
calculatingInProgress: Boolean = false
|
||||
) = annotateDiagnostics(
|
||||
file, element, holder, diagnostics, annotationByDiagnostic, annotationByTextRange,
|
||||
file, element, holder, diagnostics, highlightInfoByDiagnostic, highlightInfoByTextRange,
|
||||
::shouldSuppressUnusedParameter,
|
||||
noFixes = noFixes, calculatingInProgress = calculatingInProgress
|
||||
)
|
||||
@@ -145,9 +170,10 @@ abstract class AbstractKotlinHighlightingPass(file: KtFile, document: Document)
|
||||
* [diagnostics] has to belong to the same element
|
||||
*/
|
||||
private fun annotateQuickFixes(
|
||||
file: KtFile,
|
||||
element: PsiElement,
|
||||
diagnostics: List<Diagnostic>,
|
||||
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>
|
||||
highlightInfoByDiagnostic: MutableMap<Diagnostic, HighlightInfo>
|
||||
) {
|
||||
if (diagnostics.isEmpty()) return
|
||||
|
||||
@@ -161,63 +187,64 @@ abstract class AbstractKotlinHighlightingPass(file: KtFile, document: Document)
|
||||
if (shouldHighlightErrors) {
|
||||
ElementAnnotator(element) { param ->
|
||||
shouldSuppressUnusedParameter(param)
|
||||
}.registerDiagnosticsQuickFixes(diagnostics, annotationByDiagnostic)
|
||||
}.registerDiagnosticsQuickFixes(diagnostics, highlightInfoByDiagnostic)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun shouldSuppressUnusedParameter(parameter: KtParameter): Boolean = false
|
||||
|
||||
private fun reportErrorsToWolf() {
|
||||
if (!file.viewProvider.isPhysical) return // e.g. errors in evaluate expression
|
||||
val project: Project = file.project
|
||||
if (!PsiManager.getInstance(project).isInProject(file)) return // do not report problems in libraries
|
||||
val file: VirtualFile = file.virtualFile ?: return
|
||||
private inner class DuplicateJvmSignatureAnnotator(
|
||||
private val file: KtFile,
|
||||
private val holder: HighlightInfoHolder,
|
||||
private val otherDiagnostics: Diagnostics,
|
||||
private val moduleScope: GlobalSearchScope
|
||||
) {
|
||||
fun visit() {
|
||||
file.accept(object : PsiRecursiveElementVisitor() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
annotate(element)
|
||||
super.visitElement(element)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val wolf = WolfTheProblemSolver.getInstance(project)
|
||||
private fun annotate(element: PsiElement) {
|
||||
if (element !is KtFile && element !is KtDeclaration) return
|
||||
|
||||
val hasSyntaxErrors = wolf.hasSyntaxErrors(file)
|
||||
val problemFile = wolf.isProblemFile(file)
|
||||
val diagnostics = getJvmSignatureDiagnostics(element, otherDiagnostics, moduleScope) ?: return
|
||||
|
||||
// do nothing if file has already problems
|
||||
if (hasSyntaxErrors || problemFile) return
|
||||
val diagnosticsForElement = diagnostics.forElement(element).toSet()
|
||||
|
||||
val problems = convertToProblems(infos, file)
|
||||
wolf.reportProblems(file, problems)
|
||||
annotateDiagnostics(file, element, holder, diagnosticsForElement)
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertToProblems(
|
||||
infos: Collection<HighlightInfo>,
|
||||
file: VirtualFile,
|
||||
hasErrorElement: Boolean = true
|
||||
): List<Problem> =
|
||||
infos.filter { it.severity == HighlightSeverity.ERROR }.map { ProblemImpl(file, it, hasErrorElement) }
|
||||
|
||||
companion object {
|
||||
fun createQuickFixes(diagnostic: Diagnostic): Collection<IntentionAction> =
|
||||
createQuickFixes(listOfNotNull(diagnostic))[diagnostic]
|
||||
private val UNRESOLVED_KEY = Key<Unit>("KotlinHighlightVisitor.UNRESOLVED_KEY")
|
||||
|
||||
private val UNRESOLVED_KEY = Key<Unit>("KotlinHighlightingPass.UNRESOLVED_KEY")
|
||||
|
||||
fun wasUnresolved(element: KtNameReferenceExpression) = element.getUserData(UNRESOLVED_KEY) != null
|
||||
|
||||
fun getAfterAnalysisVisitor(holder: AnnotationHolder, bindingContext: BindingContext) = arrayOf(
|
||||
fun getAfterAnalysisVisitor(holder: HighlightInfoHolder, bindingContext: BindingContext) = arrayOf(
|
||||
PropertiesHighlightingVisitor(holder, bindingContext),
|
||||
FunctionsHighlightingVisitor(holder, bindingContext),
|
||||
VariablesHighlightingVisitor(holder, bindingContext),
|
||||
TypeKindHighlightingVisitor(holder, bindingContext)
|
||||
)
|
||||
|
||||
private fun assertBelongsToTheSameElement(element: PsiElement, diagnostics: Collection<Diagnostic>) {
|
||||
fun createQuickFixes(diagnostic: Diagnostic): Collection<IntentionAction> =
|
||||
createQuickFixes(listOfNotNull(diagnostic))[diagnostic]
|
||||
|
||||
fun wasUnresolved(element: KtNameReferenceExpression) = element.getUserData(UNRESOLVED_KEY) != null
|
||||
|
||||
internal fun assertBelongsToTheSameElement(element: PsiElement, diagnostics: Collection<Diagnostic>) {
|
||||
assert(diagnostics.all { it.psiElement == element })
|
||||
}
|
||||
|
||||
fun annotateDiagnostics(
|
||||
file: KtFile,
|
||||
element: PsiElement,
|
||||
holder: AnnotationHolder,
|
||||
holder: HighlightInfoHolder,
|
||||
diagnostics: Collection<Diagnostic>,
|
||||
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>? = null,
|
||||
annotationByTextRange: MutableMap<TextRange, Annotation>? = null,
|
||||
highlightInfoByDiagnostic: MutableMap<Diagnostic, HighlightInfo>? = null,
|
||||
highlightInfoByTextRange: MutableMap<TextRange, HighlightInfo>? = null,
|
||||
shouldSuppressUnusedParameter: (KtParameter) -> Boolean = { false },
|
||||
noFixes: Boolean = false,
|
||||
calculatingInProgress: Boolean = false
|
||||
@@ -241,16 +268,16 @@ abstract class AbstractKotlinHighlightingPass(file: KtFile, document: Document)
|
||||
shouldSuppressUnusedParameter(param)
|
||||
}
|
||||
elementAnnotator.registerDiagnosticsAnnotations(
|
||||
holder, diagnostics, annotationByDiagnostic,
|
||||
annotationByTextRange,
|
||||
holder, diagnostics, highlightInfoByDiagnostic,
|
||||
highlightInfoByTextRange,
|
||||
noFixes = noFixes, calculatingInProgress = calculatingInProgress
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun createQuickFixes(similarDiagnostics: Collection<Diagnostic>): MultiMap<Diagnostic, IntentionAction> {
|
||||
val first = similarDiagnostics.minByOrNull { it.toString() }
|
||||
val factory = similarDiagnostics.first().getRealDiagnosticFactory()
|
||||
@@ -278,6 +305,7 @@ internal fun createQuickFixes(similarDiagnostics: Collection<Diagnostic>): Multi
|
||||
return actions
|
||||
}
|
||||
|
||||
|
||||
private fun Diagnostic.getRealDiagnosticFactory(): DiagnosticFactory<*> =
|
||||
when (factory) {
|
||||
Errors.PLUGIN_ERROR -> Errors.PLUGIN_ERROR.cast(this).a.factory
|
||||
@@ -330,3 +358,4 @@ private object NoDeclarationDescriptorsChecker {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.psi.PsiElement
|
||||
@@ -28,8 +28,8 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
abstract class AfterAnalysisHighlightingVisitor protected constructor(
|
||||
holder: AnnotationHolder, protected var bindingContext: BindingContext
|
||||
) : HighlightingVisitor(holder) {
|
||||
holder: HighlightInfoHolder, protected var bindingContext: BindingContext
|
||||
) : AbstractHighlightInfoHolderHighlightingVisitor(holder) {
|
||||
|
||||
protected fun attributeKeyForDeclarationFromExtensions(element: PsiElement, descriptor: DeclarationDescriptor): TextAttributesKey? {
|
||||
@Suppress("DEPRECATION")
|
||||
|
||||
+65
-43
@@ -5,12 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction
|
||||
import com.intellij.codeInsight.intention.EmptyIntentionAction
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.lang.annotation.Annotation
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.lang.annotation.HighlightSeverity
|
||||
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.util.containers.MultiMap
|
||||
@@ -30,23 +32,24 @@ class AnnotationPresentationInfo(
|
||||
) {
|
||||
|
||||
fun processDiagnostics(
|
||||
holder: AnnotationHolder,
|
||||
holder: HighlightInfoHolder,
|
||||
diagnostics: Collection<Diagnostic>,
|
||||
annotationBuilderByDiagnostic: MutableMap<Diagnostic, Annotation>? = null,
|
||||
annotationByTextRange: MutableMap<TextRange, Annotation>?,
|
||||
highlightInfoBuilderByDiagnostic: MutableMap<Diagnostic, HighlightInfo>? = null,
|
||||
highlightInfoByTextRange: MutableMap<TextRange, HighlightInfo>?,
|
||||
fixesMap: MultiMap<Diagnostic, IntentionAction>?,
|
||||
calculatingInProgress: Boolean
|
||||
) {
|
||||
for (range in ranges) {
|
||||
for (diagnostic in diagnostics) {
|
||||
create(diagnostic, range, holder) { annotation ->
|
||||
annotationBuilderByDiagnostic?.put(diagnostic, annotation)
|
||||
create(diagnostic, range) { info ->
|
||||
holder.add(info)
|
||||
highlightInfoBuilderByDiagnostic?.put(diagnostic, info)
|
||||
if (fixesMap != null) {
|
||||
applyFixes(fixesMap, diagnostic, annotation)
|
||||
applyFixes(fixesMap, diagnostic, info)
|
||||
}
|
||||
if (calculatingInProgress && annotationByTextRange?.containsKey(range) == false) {
|
||||
annotationByTextRange[range] = annotation
|
||||
annotation.registerFix(CalculatingIntentionAction(), range)
|
||||
if (calculatingInProgress && highlightInfoByTextRange?.containsKey(range) == false) {
|
||||
highlightInfoByTextRange[range] = info
|
||||
QuickFixAction.registerQuickFixAction(info, CalculatingIntentionAction())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,55 +59,36 @@ class AnnotationPresentationInfo(
|
||||
internal fun applyFixes(
|
||||
fixesMap: MultiMap<Diagnostic, IntentionAction>,
|
||||
diagnostic: Diagnostic,
|
||||
annotation: Annotation
|
||||
info: HighlightInfo
|
||||
) {
|
||||
val fixes = fixesMap[diagnostic]
|
||||
val textRange = TextRange(annotation.startOffset, annotation.endOffset)
|
||||
fixes.forEach {
|
||||
when (it) {
|
||||
is KotlinUniversalQuickFix -> {
|
||||
annotation.registerBatchFix(it, textRange, null)
|
||||
annotation.registerFix(it, textRange)
|
||||
}
|
||||
is IntentionAction -> {
|
||||
annotation.registerFix(it, textRange)
|
||||
}
|
||||
}
|
||||
fixes.filter { it is IntentionAction || it is KotlinUniversalQuickFix }.forEach {
|
||||
QuickFixAction.registerQuickFixAction(info, it)
|
||||
}
|
||||
|
||||
if (diagnostic.severity == Severity.WARNING) {
|
||||
if (fixes.isEmpty()) {
|
||||
// if there are no quick fixes we need to register an EmptyIntentionAction to enable 'suppress' actions
|
||||
//annotation.newFix(EmptyIntentionAction(diagnostic.factory.name)).registerFix()
|
||||
annotation.registerFix(EmptyIntentionAction(diagnostic.factory.name), textRange)
|
||||
QuickFixAction.registerQuickFixAction(info, EmptyIntentionAction(diagnostic.factory.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun create(diagnostic: Diagnostic, range: TextRange, holder: AnnotationHolder, consumer: (Annotation) -> Unit) {
|
||||
val severity = when (diagnostic.severity) {
|
||||
Severity.ERROR -> HighlightSeverity.ERROR
|
||||
Severity.WARNING -> if (highlightType == ProblemHighlightType.WEAK_WARNING) {
|
||||
HighlightSeverity.WEAK_WARNING
|
||||
} else HighlightSeverity.WARNING
|
||||
Severity.INFO -> HighlightSeverity.WEAK_WARNING
|
||||
}
|
||||
|
||||
|
||||
private fun create(diagnostic: Diagnostic, range: TextRange, consumer: (HighlightInfo) -> Unit) {
|
||||
val message = nonDefaultMessage ?: getDefaultMessage(diagnostic)
|
||||
holder.newAnnotation(severity, message)
|
||||
HighlightInfo
|
||||
.newHighlightInfo(toHighlightInfoType(highlightType, diagnostic.severity))
|
||||
.range(range)
|
||||
.tooltip(getMessage(diagnostic))
|
||||
.also { builder -> highlightType?.let { builder.highlightType(it) } }
|
||||
.also { builder -> textAttributes?.let { builder.textAttributes(it) } }
|
||||
.description(message)
|
||||
.escapedToolTip(getMessage(diagnostic))
|
||||
.also { builder -> textAttributes?.let(builder::textAttributes) ?: convertSeverityTextAttributes(highlightType, diagnostic.severity)?.let(builder::textAttributes) }
|
||||
.also {
|
||||
if (diagnostic.severity == Severity.WARNING) {
|
||||
it.problemGroup(KotlinSuppressableWarningProblemGroup(diagnostic.factory))
|
||||
}
|
||||
}
|
||||
.create()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(holder as? List<Annotation>)?.last()?.let(consumer::invoke)
|
||||
.createUnconditionally()
|
||||
.also(consumer)
|
||||
}
|
||||
|
||||
private fun getMessage(diagnostic: Diagnostic): String {
|
||||
@@ -132,4 +116,42 @@ class AnnotationPresentationInfo(
|
||||
}
|
||||
}
|
||||
|
||||
private fun toHighlightInfoType(highlightType: ProblemHighlightType?, severity: Severity): HighlightInfoType =
|
||||
when (highlightType) {
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL -> HighlightInfoType.UNUSED_SYMBOL
|
||||
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL -> HighlightInfoType.WRONG_REF
|
||||
ProblemHighlightType.LIKE_DEPRECATED -> HighlightInfoType.DEPRECATED
|
||||
ProblemHighlightType.LIKE_MARKED_FOR_REMOVAL -> HighlightInfoType.MARKED_FOR_REMOVAL
|
||||
else -> convertSeverity(highlightType, severity)
|
||||
}
|
||||
|
||||
private fun convertSeverity(highlightType: ProblemHighlightType?, severity: Severity): HighlightInfoType =
|
||||
when (severity) {
|
||||
Severity.ERROR -> HighlightInfoType.ERROR
|
||||
Severity.WARNING -> {
|
||||
if (highlightType == ProblemHighlightType.WEAK_WARNING) {
|
||||
HighlightInfoType.WEAK_WARNING
|
||||
} else HighlightInfoType.WARNING
|
||||
}
|
||||
Severity.INFO -> HighlightInfoType.WEAK_WARNING
|
||||
else -> HighlightInfoType.INFORMATION
|
||||
}
|
||||
|
||||
private fun convertSeverityTextAttributes(highlightType: ProblemHighlightType?, severity: Severity): TextAttributesKey? =
|
||||
when (highlightType) {
|
||||
null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING ->
|
||||
when (severity) {
|
||||
Severity.ERROR -> CodeInsightColors.ERRORS_ATTRIBUTES
|
||||
Severity.WARNING -> {
|
||||
if (highlightType == ProblemHighlightType.WEAK_WARNING) {
|
||||
CodeInsightColors.WEAK_WARNING_ATTRIBUTES
|
||||
} else CodeInsightColors.WARNINGS_ATTRIBUTES
|
||||
}
|
||||
Severity.INFO -> CodeInsightColors.WARNINGS_ATTRIBUTES
|
||||
else -> null
|
||||
}
|
||||
ProblemHighlightType.GENERIC_ERROR -> CodeInsightColors.ERRORS_ATTRIBUTES
|
||||
else -> null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.highlighter
|
||||
|
||||
import com.intellij.codeHighlighting.*
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.lang.annotation.Annotator
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.asJava.getJvmSignatureDiagnostics
|
||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
|
||||
import org.jetbrains.kotlin.idea.highlighter.AbstractKotlinHighlightingPass.Companion.annotateDiagnostics
|
||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class DuplicateJvmSignatureHighlightPass(file: KtFile, document: Document) :
|
||||
AbstractBindingContextAwareHighlightingPassBase(file, document) {
|
||||
override val annotator: Annotator
|
||||
get() = DuplicateJvmSignatureAnnotator()
|
||||
|
||||
inner class DuplicateJvmSignatureAnnotator : Annotator {
|
||||
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
|
||||
if (element !is KtFile && element !is KtDeclaration) return
|
||||
|
||||
val otherDiagnostics = when (element) {
|
||||
is KtDeclaration -> element.analyzeWithContent()
|
||||
is KtFile -> element.analyzeWithContent()
|
||||
else -> throw AssertionError("DuplicateJvmSignatureAnnotator: should not get here! Element: ${element.text}")
|
||||
}.diagnostics
|
||||
|
||||
val moduleScope = element.getModuleInfo().contentScope()
|
||||
val diagnostics = getJvmSignatureDiagnostics(element, otherDiagnostics, moduleScope) ?: return
|
||||
|
||||
val diagnosticsForElement = diagnostics.forElement(element).toSet()
|
||||
|
||||
annotateDiagnostics(file, element, holder, diagnosticsForElement)
|
||||
}
|
||||
}
|
||||
|
||||
class Factory : TextEditorHighlightingPassFactory {
|
||||
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? {
|
||||
return if (file is KtFile &&
|
||||
ProjectRootsUtil.isInProjectSource(file) &&
|
||||
TargetPlatformDetector.getPlatform(file).isJvm()
|
||||
) {
|
||||
DuplicateJvmSignatureHighlightPass(file, editor.document)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Registrar : TextEditorHighlightingPassFactoryRegistrar {
|
||||
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
|
||||
registrar.registerTextEditorHighlightingPass(
|
||||
Factory(),
|
||||
/* runAfterCompletionOf = */ intArrayOf(Pass.UPDATE_ALL),
|
||||
/* runAfterStartingOf = */ null,
|
||||
/* runIntentionsPassAfter = */ false,
|
||||
/* forcedPassId = */ -1
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter
|
||||
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.lang.annotation.Annotation
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.openapi.diagnostic.ControlFlowException
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||
@@ -31,10 +32,10 @@ internal class ElementAnnotator(
|
||||
private val shouldSuppressUnusedParameter: (KtParameter) -> Boolean
|
||||
) {
|
||||
fun registerDiagnosticsAnnotations(
|
||||
holder: AnnotationHolder,
|
||||
holder: HighlightInfoHolder,
|
||||
diagnostics: Collection<Diagnostic>,
|
||||
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>?,
|
||||
annotationByTextRange: MutableMap<TextRange, Annotation>?,
|
||||
highlightInfoByDiagnostic: MutableMap<Diagnostic, HighlightInfo>?,
|
||||
highlightInfoByTextRange: MutableMap<TextRange, HighlightInfo>?,
|
||||
noFixes: Boolean,
|
||||
calculatingInProgress: Boolean
|
||||
) = diagnostics.groupBy { it.factory }
|
||||
@@ -42,18 +43,18 @@ internal class ElementAnnotator(
|
||||
registerSameFactoryDiagnosticsAnnotations(
|
||||
holder,
|
||||
it.value,
|
||||
annotationByDiagnostic,
|
||||
annotationByTextRange,
|
||||
highlightInfoByDiagnostic,
|
||||
highlightInfoByTextRange,
|
||||
noFixes,
|
||||
calculatingInProgress
|
||||
)
|
||||
}
|
||||
|
||||
private fun registerSameFactoryDiagnosticsAnnotations(
|
||||
holder: AnnotationHolder,
|
||||
holder: HighlightInfoHolder,
|
||||
diagnostics: Collection<Diagnostic>,
|
||||
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>?,
|
||||
annotationByTextRange: MutableMap<TextRange, Annotation>?,
|
||||
highlightInfoByDiagnostic: MutableMap<Diagnostic, HighlightInfo>?,
|
||||
highlightInfoByTextRange: MutableMap<TextRange, HighlightInfo>?,
|
||||
noFixes: Boolean,
|
||||
calculatingInProgress: Boolean
|
||||
) {
|
||||
@@ -62,8 +63,8 @@ internal class ElementAnnotator(
|
||||
holder,
|
||||
diagnostics,
|
||||
presentationInfo,
|
||||
annotationByDiagnostic,
|
||||
annotationByTextRange,
|
||||
highlightInfoByDiagnostic,
|
||||
highlightInfoByTextRange,
|
||||
noFixes,
|
||||
calculatingInProgress
|
||||
)
|
||||
@@ -71,21 +72,21 @@ internal class ElementAnnotator(
|
||||
|
||||
fun registerDiagnosticsQuickFixes(
|
||||
diagnostics: List<Diagnostic>,
|
||||
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>
|
||||
highlightInfoByDiagnostic: MutableMap<Diagnostic, HighlightInfo>
|
||||
) = diagnostics.groupBy { it.factory }
|
||||
.forEach { registerDiagnosticsSameFactoryQuickFixes(it.value, annotationByDiagnostic) }
|
||||
.forEach { registerDiagnosticsSameFactoryQuickFixes(it.value, highlightInfoByDiagnostic) }
|
||||
|
||||
private fun registerDiagnosticsSameFactoryQuickFixes(
|
||||
diagnostics: List<Diagnostic>,
|
||||
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>
|
||||
highlightInfoByDiagnostic: MutableMap<Diagnostic, HighlightInfo>
|
||||
) {
|
||||
val presentationInfo = presentationInfo(diagnostics) ?: return
|
||||
val fixesMap = createFixesMap(diagnostics) ?: return
|
||||
|
||||
diagnostics.forEach {
|
||||
val annotation = annotationByDiagnostic[it] ?: return
|
||||
val highlightInfo = highlightInfoByDiagnostic[it] ?: return
|
||||
|
||||
presentationInfo.applyFixes(fixesMap, it, annotation)
|
||||
presentationInfo.applyFixes(fixesMap, it, highlightInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,18 +162,18 @@ internal class ElementAnnotator(
|
||||
}
|
||||
|
||||
private fun setUpAnnotations(
|
||||
holder: AnnotationHolder,
|
||||
holder: HighlightInfoHolder,
|
||||
diagnostics: Collection<Diagnostic>,
|
||||
data: AnnotationPresentationInfo,
|
||||
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>?,
|
||||
annotationByTextRange: MutableMap<TextRange, Annotation>?,
|
||||
highlightInfoByDiagnostic: MutableMap<Diagnostic, HighlightInfo>?,
|
||||
highlightInfoByTextRange: MutableMap<TextRange, HighlightInfo>?,
|
||||
noFixes: Boolean,
|
||||
calculatingInProgress: Boolean
|
||||
) {
|
||||
val fixesMap =
|
||||
createFixesMap(diagnostics, noFixes)
|
||||
|
||||
data.processDiagnostics(holder, diagnostics, annotationByDiagnostic, annotationByTextRange, fixesMap, calculatingInProgress)
|
||||
data.processDiagnostics(holder, diagnostics, highlightInfoByDiagnostic, highlightInfoByTextRange, fixesMap, calculatingInProgress)
|
||||
}
|
||||
|
||||
private fun createFixesMap(
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
|
||||
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.serialization.deserialization.KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
internal class FunctionsHighlightingVisitor(holder: AnnotationHolder, bindingContext: BindingContext) :
|
||||
internal class FunctionsHighlightingVisitor(holder: HighlightInfoHolder, bindingContext: BindingContext) :
|
||||
AfterAnalysisHighlightingVisitor(holder, bindingContext) {
|
||||
|
||||
override fun visitBinaryExpression(expression: KtBinaryExpression) {
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.psi.PsiElement
|
||||
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.isSynthesized
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
internal class PropertiesHighlightingVisitor(holder: AnnotationHolder, bindingContext: BindingContext) :
|
||||
internal class PropertiesHighlightingVisitor(holder: HighlightInfoHolder, bindingContext: BindingContext) :
|
||||
AfterAnalysisHighlightingVisitor(holder, bindingContext) {
|
||||
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
|
||||
internal class TypeKindHighlightingVisitor(holder: AnnotationHolder, bindingContext: BindingContext) :
|
||||
internal class TypeKindHighlightingVisitor(holder: HighlightInfoHolder, bindingContext: BindingContext) :
|
||||
AfterAnalysisHighlightingVisitor(holder, bindingContext) {
|
||||
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.types.expressions.CaptureKind
|
||||
|
||||
internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingContext: BindingContext) :
|
||||
internal class VariablesHighlightingVisitor(holder: HighlightInfoHolder, bindingContext: BindingContext) :
|
||||
AfterAnalysisHighlightingVisitor(holder, bindingContext) {
|
||||
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
|
||||
Reference in New Issue
Block a user