FIR IDE: Move base part of highlighting to frontend-independent-module

This commit is contained in:
Ilya Kirillov
2020-05-24 16:36:44 +03:00
parent 77550186ad
commit 6a4fa8de9d
13 changed files with 216 additions and 153 deletions
@@ -10,14 +10,11 @@ import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.lang.annotation.Annotation import com.intellij.lang.annotation.Annotation
import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.colors.TextAttributesKey import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import com.intellij.xml.util.XmlStringUtil
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
class AnnotationPresentationInfo( class AnnotationPresentationInfo(
@@ -52,55 +49,14 @@ class AnnotationPresentationInfo(
} }
} }
private fun create(diagnostic: Diagnostic, range: TextRange, holder: AnnotationHolder): Annotation { private fun create(diagnostic: Diagnostic, range: TextRange, holder: AnnotationHolder): Annotation =
val defaultMessage = nonDefaultMessage ?: getDefaultMessage(diagnostic) Diagnostic2Annotation.createAnnotation(
diagnostic,
val annotation = when (diagnostic.severity) { range,
Severity.ERROR -> holder.createErrorAnnotation(range, defaultMessage) holder,
Severity.WARNING -> { nonDefaultMessage,
if (highlightType == ProblemHighlightType.WEAK_WARNING) { textAttributes,
holder.createWeakWarningAnnotation(range, defaultMessage) highlightType,
} else { IdeErrorMessages::render
holder.createWarningAnnotation(range, defaultMessage) )
}
}
Severity.INFO -> holder.createInfoAnnotation(range, defaultMessage)
}
annotation.tooltip = getMessage(diagnostic)
if (highlightType != null) {
annotation.highlightType = highlightType
}
if (textAttributes != null) {
annotation.textAttributes = textAttributes
}
return annotation
}
private fun getMessage(diagnostic: Diagnostic): String {
var message = IdeErrorMessages.render(diagnostic)
if (ApplicationManager.getApplication().isInternal || ApplicationManager.getApplication().isUnitTestMode) {
val factoryName = diagnostic.factory.name
message = if (message.startsWith("<html>")) {
"<html>[$factoryName] ${message.substring("<html>".length)}"
} else {
"[$factoryName] $message"
}
}
if (!message.startsWith("<html>")) {
message = "<html><body>${XmlStringUtil.escapeString(message)}</body></html>"
}
return message
}
private fun getDefaultMessage(diagnostic: Diagnostic): String {
val message = DefaultErrorMessages.render(diagnostic)
if (ApplicationManager.getApplication().isInternal || ApplicationManager.getApplication().isUnitTestMode) {
return "[${diagnostic.factory.name}] $message"
}
return message
}
} }
@@ -5,11 +5,9 @@
package org.jetbrains.kotlin.idea.highlighter package org.jetbrains.kotlin.idea.highlighter
import com.intellij.codeInsight.daemon.impl.HighlightRangeExtension
import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.ControlFlowException
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.colors.CodeInsightColors import com.intellij.openapi.editor.colors.CodeInsightColors
@@ -17,7 +15,6 @@ import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.MultiRangeReference import com.intellij.psi.MultiRangeReference
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
@@ -27,36 +24,23 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
import org.jetbrains.kotlin.idea.fir.FirResolution
import org.jetbrains.kotlin.idea.fir.firResolveState
import org.jetbrains.kotlin.idea.fir.getOrBuildFirWithDiagnostics
import org.jetbrains.kotlin.idea.quickfix.QuickFixes import org.jetbrains.kotlin.idea.quickfix.QuickFixes
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.lang.reflect.* import java.lang.reflect.*
import java.util.* import java.util.*
open class KotlinPsiChecker : Annotator, HighlightRangeExtension { open class KotlinPsiChecker : AbstractKotlinPsiChecker() {
override fun shouldHighlight(file: KtFile): Boolean = KotlinHighlightingUtil.shouldHighlight(file)
override fun annotate(element: PsiElement, holder: AnnotationHolder) { override fun annotateElement(
val file = element.containingFile as? KtFile ?: return
if (!KotlinHighlightingUtil.shouldHighlight(file)) return
//todo move all fir stuff to fir plugin
if (FirResolution.enabled) {
annotateElementUsingFrontendIR(element, file, holder)
} else {
annotateElement(element, file, holder)
}
}
private fun annotateElement(
element: PsiElement, element: PsiElement,
containingFile: KtFile, containingFile: KtFile,
holder: AnnotationHolder holder: AnnotationHolder
@@ -73,29 +57,6 @@ open class KotlinPsiChecker : Annotator, HighlightRangeExtension {
annotateElement(element, holder, bindingContext.diagnostics) annotateElement(element, holder, bindingContext.diagnostics)
} }
private fun annotateElementUsingFrontendIR(
element: PsiElement,
containingFile: KtFile,
holder: AnnotationHolder
) {
if (element !is KtElement) return
val state = containingFile.firResolveState()
containingFile.getOrBuildFirWithDiagnostics(state)
val diagnostics = state.getDiagnostics(element)
if (diagnostics.isEmpty()) return
if (KotlinHighlightingUtil.shouldHighlightErrors(element)) {
ElementAnnotator(element, holder) { param ->
shouldSuppressUnusedParameter(param)
}.registerDiagnosticsAnnotations(diagnostics)
}
}
override fun isForceHighlightParents(file: PsiFile): Boolean {
return file is KtFile
}
protected open fun shouldSuppressUnusedParameter(parameter: KtParameter): Boolean = false protected open fun shouldSuppressUnusedParameter(parameter: KtParameter): Boolean = false
fun annotateElement(element: PsiElement, holder: AnnotationHolder, diagnostics: Diagnostics) { fun annotateElement(element: PsiElement, holder: AnnotationHolder, diagnostics: Diagnostics) {
@@ -1,25 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.highlighter
import org.jetbrains.annotations.TestOnly
object NameHighlighter {
var namesHighlightingEnabled = true
@TestOnly set
}
@@ -0,0 +1,63 @@
/*
* 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.lang.annotation.AnnotationHolder
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.fir.firResolveState
import org.jetbrains.kotlin.idea.fir.getOrBuildFirWithDiagnostics
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
class KotlinFirPsiChecker : AbstractKotlinPsiChecker() {
override fun shouldHighlight(file: KtFile): Boolean {
return true // todo
}
override fun annotateElement(element: PsiElement, containingFile: KtFile, holder: AnnotationHolder) {
if (element !is KtElement) return
val state = containingFile.firResolveState()
containingFile.getOrBuildFirWithDiagnostics(state)
val diagnostics = state.getDiagnostics(element)
if (diagnostics.isEmpty()) return
if (shouldHighlightErrors(element)) {
highlightDiagnostics(diagnostics, holder)
}
}
private fun highlightDiagnostics(diagnostics: Collection<Diagnostic>, holder: AnnotationHolder) {
diagnostics.groupBy { it.factory }.forEach { group -> registerDiagnosticAnnotations(group.value, holder) }
}
private fun registerDiagnosticAnnotations(diagnostics: List<Diagnostic>, holder: AnnotationHolder) {
assert(diagnostics.isNotEmpty())
val diagnostic = diagnostics.first()
val ranges = diagnostic.textRanges
ranges.forEach { range ->
diagnostics.forEach { diagnostic ->
Diagnostic2Annotation.createAnnotation(
diagnostic,
range,
holder,
nonDefaultMessage = null,
textAttributes = null,
highlightType = null,
renderMessage = IdeErrorMessages::render
)
}
}
}
private fun shouldHighlightErrors(element: KtElement): Boolean {
return true // todo
}
}
@@ -12,6 +12,7 @@ dependencies {
*/ */
compileOnly(project(":compiler:frontend")) // we need caches form here to work with ModuleInfo :( compileOnly(project(":compiler:frontend")) // we need caches form here to work with ModuleInfo :(
compileOnly(project(":idea:idea-jps-common"))
compileOnly(project(":compiler:psi")) compileOnly(project(":compiler:psi"))
compileOnly(project(":kotlin-reflect-api")) compileOnly(project(":kotlin-reflect-api"))
@@ -0,0 +1,29 @@
/*
* 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.HighlightRangeExtension
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.psi.KtFile
abstract class AbstractKotlinPsiChecker : Annotator, HighlightRangeExtension {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
val file = element.containingFile as? KtFile ?: return
if (!shouldHighlight(file)) return
annotateElement(element, file, holder)
}
override fun isForceHighlightParents(file: PsiFile): Boolean {
return file is KtFile
}
protected abstract fun shouldHighlight(file: KtFile): Boolean
protected abstract fun annotateElement(element: PsiElement, containingFile: KtFile, holder: AnnotationHolder)
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -0,0 +1,79 @@
/*
* 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.codeInspection.ProblemHighlightType
import com.intellij.lang.annotation.Annotation
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.util.TextRange
import com.intellij.xml.util.XmlStringUtil
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
object Diagnostic2Annotation {
fun getHtmlMessage(diagnostic: Diagnostic, renderMessage: (Diagnostic) -> String): String {
var message = renderMessage(diagnostic)
if (ApplicationManager.getApplication().isInternal || ApplicationManager.getApplication().isUnitTestMode) {
val factoryName = diagnostic.factory.name
message = if (message.startsWith("<html>")) {
"<html>[$factoryName] ${message.substring("<html>".length)}"
} else {
"[$factoryName] $message"
}
}
if (!message.startsWith("<html>")) {
message = "<html><body>${XmlStringUtil.escapeString(message)}</body></html>"
}
return message
}
fun getDefaultMessage(diagnostic: Diagnostic): String {
val message = DefaultErrorMessages.render(diagnostic)
if (ApplicationManager.getApplication().isInternal || ApplicationManager.getApplication().isUnitTestMode) {
return "[${diagnostic.factory.name}] $message"
}
return message
}
fun createAnnotation(
diagnostic: Diagnostic,
range: TextRange,
holder: AnnotationHolder,
nonDefaultMessage: String?,
textAttributes: TextAttributesKey?,
highlightType: ProblemHighlightType?,
renderMessage: (Diagnostic) -> String
): Annotation {
val defaultMessage = nonDefaultMessage ?: getDefaultMessage(diagnostic)
val annotation = when (diagnostic.severity) {
Severity.ERROR -> holder.createErrorAnnotation(range, defaultMessage)
Severity.WARNING -> {
if (highlightType == ProblemHighlightType.WEAK_WARNING) {
holder.createWeakWarningAnnotation(range, defaultMessage)
} else {
holder.createWarningAnnotation(range, defaultMessage)
}
}
Severity.INFO -> holder.createInfoAnnotation(range, defaultMessage)
}
annotation.tooltip = getHtmlMessage(diagnostic, renderMessage)
if (highlightType != null) {
annotation.highlightType = highlightType
}
if (textAttributes != null) {
annotation.textAttributes = textAttributes
}
return annotation
}
}
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * 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.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.highlighter package org.jetbrains.kotlin.idea.highlighter
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -12,6 +12,8 @@ import com.intellij.codeInsight.daemon.impl.UpdateHighlightersUtil
import com.intellij.lang.annotation.AnnotationSession import com.intellij.lang.annotation.AnnotationSession
import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
@@ -19,6 +21,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiRecursiveElementVisitor import com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
class KotlinBeforeResolveHighlightingPass( class KotlinBeforeResolveHighlightingPass(
private val file: KtFile, private val file: KtFile,
@@ -67,4 +70,8 @@ class KotlinBeforeResolveHighlightingPass(
) )
} }
} }
// companion object {
// val EP_NAME = ExtensionPointName.create<HighlightingVisitor>("org.jetbrains.kotlin.beforeResolveHighlightingVisitor")
// }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * 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.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.highlighter; package org.jetbrains.kotlin.idea.highlighter;
@@ -0,0 +1,14 @@
/*
* 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 org.jetbrains.annotations.TestOnly
object NameHighlighter {
var namesHighlightingEnabled = true
@TestOnly set
}