New highlighting/line markers/diagnostics test infrastructure
The current test framework has a number of shortcomings, such as different markups for different kinds, those formats can not be mixed in one test file, lacks composability, re-use of it is complicated This commit contains the first version of the new test runner to which tests will be migrated in the future.
This commit is contained in:
@@ -62,7 +62,7 @@ object CheckerTestUtil {
|
||||
private const val IGNORE_DIAGNOSTIC_PARAMETER = "IGNORE"
|
||||
private const val INDIVIDUAL_DIAGNOSTIC = """(\w+;)?(\w+:)?(\w+)(?:\(((?:".*?")(?:,\s*".*?")*)\))?"""
|
||||
|
||||
internal val rangeStartOrEndPattern = Pattern.compile("(<!$INDIVIDUAL_DIAGNOSTIC(,\\s*$INDIVIDUAL_DIAGNOSTIC)*!>)|(<!>)")
|
||||
val rangeStartOrEndPattern = Pattern.compile("(<!$INDIVIDUAL_DIAGNOSTIC(,\\s*$INDIVIDUAL_DIAGNOSTIC)*!>)|(<!>)")
|
||||
val individualDiagnosticPattern: Pattern = Pattern.compile(INDIVIDUAL_DIAGNOSTIC)
|
||||
|
||||
fun getDiagnosticsIncludingSyntaxErrors(
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.codeMetaInfo
|
||||
|
||||
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
|
||||
import com.intellij.lang.annotation.HighlightSeverity
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.PsiManagerImpl
|
||||
import com.intellij.psi.impl.cache.CacheManager
|
||||
import com.intellij.psi.impl.source.tree.PsiErrorElementImpl
|
||||
import com.intellij.psi.impl.source.tree.TreeElement
|
||||
import com.intellij.psi.impl.source.tree.TreeUtil
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.UsageSearchContext
|
||||
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
|
||||
import com.intellij.testFramework.runInEdtAndWait
|
||||
import com.intellij.util.io.exists
|
||||
import gnu.trove.TIntArrayList
|
||||
import org.jetbrains.kotlin.checkers.BaseDiagnosticsTest
|
||||
import org.jetbrains.kotlin.checkers.diagnostics.SyntaxErrorDiagnostic
|
||||
import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
|
||||
import org.jetbrains.kotlin.checkers.utils.DiagnosticsRenderingConfiguration
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.diagnostics.AbstractDiagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.models.CodeMetaInfoFactory
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.models.DiagnosticCodeMetaInfo
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.models.HighlightingCodeMetaInfo
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.models.ICodeMetaInfo
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.AbstractCodeMetaInfoRenderConfiguration
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.DiagnosticCodeMetaInfoRenderConfiguration
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.HighlightingRenderConfiguration
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.LineMarkerRenderConfiguration
|
||||
import org.jetbrains.kotlin.idea.multiplatform.setupMppProjectFromTextFile
|
||||
import org.jetbrains.kotlin.idea.resolve.getDataFlowValueFactory
|
||||
import org.jetbrains.kotlin.idea.resolve.getLanguageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.stubs.AbstractMultiModuleTest
|
||||
import org.jetbrains.kotlin.idea.util.sourceRoots
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
|
||||
class CodeMetaInfoTestCase(val codeMetaInfoTypes: Collection<AbstractCodeMetaInfoRenderConfiguration>) : DaemonAnalyzerTestCase() {
|
||||
|
||||
fun getDiagnosticCodeMetaInfos(
|
||||
configuration: DiagnosticCodeMetaInfoRenderConfiguration?,
|
||||
parseDirective: Boolean = true
|
||||
): Collection<ICodeMetaInfo> {
|
||||
val tempSourceKtFile = PsiManager.getInstance(project).findFile(file.virtualFile) as KtFile
|
||||
val resolutionFacade = tempSourceKtFile.getResolutionFacade()
|
||||
val (bindingContext, moduleDescriptor) = resolutionFacade.analyzeWithAllCompilerChecks(listOf(tempSourceKtFile))
|
||||
val directives = KotlinTestUtils.parseDirectives(file.text)
|
||||
val diagnosticsFilter = BaseDiagnosticsTest.parseDiagnosticFilterDirective(directives, allowUnderscoreUsage = false)
|
||||
val diagnostics = CheckerTestUtil.getDiagnosticsIncludingSyntaxErrors(
|
||||
bindingContext,
|
||||
file,
|
||||
markDynamicCalls = false,
|
||||
dynamicCallDescriptors = mutableListOf(),
|
||||
configuration = DiagnosticsRenderingConfiguration(
|
||||
platform = null, // we don't need to attach platform-description string to diagnostic here
|
||||
withNewInference = false,
|
||||
languageVersionSettings = resolutionFacade.getLanguageVersionSettings(),
|
||||
),
|
||||
dataFlowValueFactory = resolutionFacade.getDataFlowValueFactory(),
|
||||
moduleDescriptor = moduleDescriptor as ModuleDescriptorImpl
|
||||
).map { it.diagnostic }.filter { !parseDirective || diagnosticsFilter.value(it) }
|
||||
configuration?.renderParams = directives.contains(BaseDiagnosticsTest.RENDER_DIAGNOSTICS_MESSAGES)
|
||||
return CodeMetaInfoFactory.getCodeMetaInfo(diagnostics, configuration)
|
||||
}
|
||||
|
||||
fun getLineMarkerCodeMetaInfos(configuration: LineMarkerRenderConfiguration): Collection<ICodeMetaInfo> {
|
||||
if ("!CHECK_HIGHLIGHTING" in file.text)
|
||||
return emptyList()
|
||||
|
||||
CodeInsightTestFixtureImpl.instantiateAndRun(file, editor, TIntArrayList().toNativeArray(), false)
|
||||
val lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(getDocument(file), project)
|
||||
return CodeMetaInfoFactory.getCodeMetaInfo(lineMarkers, configuration)
|
||||
}
|
||||
|
||||
fun getHighlightingCodeMetaInfos(configuration: HighlightingRenderConfiguration?): Collection<ICodeMetaInfo> {
|
||||
val infos = CodeInsightTestFixtureImpl.instantiateAndRun(file, editor, TIntArrayList().toNativeArray(), false)
|
||||
|
||||
return CodeMetaInfoFactory.getCodeMetaInfo(infos, configuration)
|
||||
}
|
||||
|
||||
fun checkFile(expectedFile: File, project: Project, editor: Editor) {
|
||||
myProject = project
|
||||
myPsiManager = PsiManager.getInstance(myProject) as PsiManagerImpl
|
||||
runInEdtAndWait {
|
||||
setActiveEditor(editor)
|
||||
check(expectedFile)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkFile(file: VirtualFile, expectedFile: File, project: Project) {
|
||||
myProject = project
|
||||
myPsiManager = PsiManager.getInstance(myProject) as PsiManagerImpl
|
||||
configureByExistingFile(file)
|
||||
check(expectedFile)
|
||||
}
|
||||
|
||||
fun check(expectedFile: File) {
|
||||
|
||||
val codeMetaInfoForCheck = mutableListOf<ICodeMetaInfo>()
|
||||
PsiDocumentManager.getInstance(myProject).commitAllDocuments()
|
||||
|
||||
//to load text
|
||||
ApplicationManager.getApplication().runWriteAction { TreeUtil.clearCaches(myFile.node as TreeElement) }
|
||||
|
||||
//to initialize caches
|
||||
if (!DumbService.isDumb(myProject)) {
|
||||
CacheManager.SERVICE.getInstance(myProject)
|
||||
.getFilesWithWord("XXX", UsageSearchContext.IN_COMMENTS, GlobalSearchScope.allScope(myProject), true)
|
||||
}
|
||||
|
||||
for (configuration in codeMetaInfoTypes) {
|
||||
when (configuration) {
|
||||
is DiagnosticCodeMetaInfoRenderConfiguration -> {
|
||||
codeMetaInfoForCheck.addAll(getDiagnosticCodeMetaInfos(configuration))
|
||||
}
|
||||
is HighlightingRenderConfiguration -> {
|
||||
codeMetaInfoForCheck.addAll(getHighlightingCodeMetaInfos(configuration))
|
||||
}
|
||||
is LineMarkerRenderConfiguration -> {
|
||||
codeMetaInfoForCheck.addAll(getLineMarkerCodeMetaInfos(configuration))
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unexpected code meta info configuration: $configuration")
|
||||
}
|
||||
}
|
||||
if (codeMetaInfoTypes.any { it is DiagnosticCodeMetaInfoRenderConfiguration } &&
|
||||
!codeMetaInfoTypes.any { it is HighlightingRenderConfiguration }
|
||||
) {
|
||||
checkHighlightErrorItemsInDiagnostics(
|
||||
getDiagnosticCodeMetaInfos(null, false).filterIsInstance<DiagnosticCodeMetaInfo>()
|
||||
)
|
||||
}
|
||||
|
||||
val textWithCodeMetaInfo = CodeMetaInfoRenderer.renderTagsToText(codeMetaInfoForCheck, myEditor.document.text)
|
||||
KotlinTestUtils.assertEqualsToFile(
|
||||
expectedFile,
|
||||
textWithCodeMetaInfo.toString()
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkHighlightErrorItemsInDiagnostics(
|
||||
diagnostics: Collection<DiagnosticCodeMetaInfo>
|
||||
) {
|
||||
val highlightItems: List<ICodeMetaInfo> =
|
||||
getHighlightingCodeMetaInfos(null).filter { (it as HighlightingCodeMetaInfo).highlightingInfo.severity == HighlightSeverity.ERROR }
|
||||
|
||||
highlightItems.forEach { highlightingCodeMetaInfo ->
|
||||
assert(
|
||||
diagnostics.any { diagnosticCodeMetaInfo ->
|
||||
diagnosticCodeMetaInfo.start == highlightingCodeMetaInfo.start &&
|
||||
when (diagnosticCodeMetaInfo.diagnostic) {
|
||||
is SyntaxErrorDiagnostic -> {
|
||||
val diagnostic: SyntaxErrorDiagnostic = diagnosticCodeMetaInfo.diagnostic
|
||||
(highlightingCodeMetaInfo as HighlightingCodeMetaInfo).highlightingInfo.description in (diagnostic.psiElement as PsiErrorElementImpl).errorDescription
|
||||
}
|
||||
is AbstractDiagnostic<*> -> {
|
||||
val diagnostic: AbstractDiagnostic<*> = diagnosticCodeMetaInfo.diagnostic
|
||||
diagnostic.factory.toString() in (highlightingCodeMetaInfo as HighlightingCodeMetaInfo).highlightingInfo.description
|
||||
}
|
||||
else -> throw java.lang.IllegalArgumentException("Unknown diagnostic type: ${diagnosticCodeMetaInfo.diagnostic}")
|
||||
}
|
||||
},
|
||||
) { "Could not find DIAGNOSTIC for ${(highlightingCodeMetaInfo as HighlightingCodeMetaInfo).highlightingInfo}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractDiagnosticCodeMetaInfoTest : AbstractCodeMetaInfoTest() {
|
||||
override fun getConfigurations() = listOf(
|
||||
DiagnosticCodeMetaInfoRenderConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
abstract class AbstractLineMarkerCodeMetaInfoTest : AbstractCodeMetaInfoTest() {
|
||||
override fun getConfigurations() = listOf(
|
||||
LineMarkerRenderConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
abstract class AbstractHighlightingCodeMetaInfoTest : AbstractCodeMetaInfoTest() {
|
||||
override fun getConfigurations() = listOf(
|
||||
HighlightingRenderConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
abstract class AbstractCodeMetaInfoTest : AbstractMultiModuleTest() {
|
||||
|
||||
open fun getConfigurations() = listOf(
|
||||
DiagnosticCodeMetaInfoRenderConfiguration(),
|
||||
LineMarkerRenderConfiguration(),
|
||||
HighlightingRenderConfiguration()
|
||||
)
|
||||
|
||||
protected open fun setupProject(testDataPath: String) {
|
||||
val dependenciesTxt = File(testDataPath, "dependencies.txt")
|
||||
require(dependenciesTxt.exists()) {
|
||||
"${dependenciesTxt.absolutePath} does not exist. dependencies.txt is required"
|
||||
}
|
||||
setupMppProjectFromTextFile(File(testDataPath))
|
||||
}
|
||||
|
||||
fun doTest(testDataPath: String) {
|
||||
val testRoot = File(testDataPath)
|
||||
val checker = CodeMetaInfoTestCase(getConfigurations())
|
||||
setupProject(testDataPath)
|
||||
|
||||
for (module in ModuleManager.getInstance(project).modules) {
|
||||
for (sourceRoot in module.sourceRoots) {
|
||||
VfsUtilCore.processFilesRecursively(sourceRoot) { file ->
|
||||
if (file.isDirectory) return@processFilesRecursively true
|
||||
|
||||
checker.checkFile(file, file.findCorrespondingFileInTestDir(sourceRoot, testRoot), project)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun VirtualFile.findCorrespondingFileInTestDir(containingRoot: VirtualFile, testDir: File): File {
|
||||
val tempRootPath = Paths.get(containingRoot.path)
|
||||
val tempProjectDirPath = tempRootPath.parent
|
||||
val tempSourcePath = Paths.get(path)
|
||||
|
||||
val relativeToProjectRootPath = tempProjectDirPath.relativize(tempSourcePath)
|
||||
|
||||
val testSourcesProjectDirPath = testDir.toPath()
|
||||
val testSourcePath = testSourcesProjectDirPath.resolve(relativeToProjectRootPath)
|
||||
|
||||
require(testSourcePath.exists()) {
|
||||
"Can't find file in testdata for copied file $this: checked at path ${testSourcePath.toAbsolutePath()}"
|
||||
}
|
||||
|
||||
return testSourcePath.toFile()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.codeMetaInfo
|
||||
|
||||
import com.intellij.util.containers.Stack
|
||||
import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.models.ICodeMetaInfo
|
||||
import java.io.File
|
||||
|
||||
object CodeMetaInfoRenderer {
|
||||
|
||||
fun renderTagsToText(
|
||||
codeMetaInfos: List<ICodeMetaInfo>,
|
||||
originalText: String
|
||||
): StringBuffer {
|
||||
val result = StringBuffer()
|
||||
if (codeMetaInfos.isEmpty()) {
|
||||
result.append(originalText)
|
||||
return result
|
||||
}
|
||||
val sortedMetaInfos = getSortedCodeMetaInfos(codeMetaInfos)
|
||||
val opened = Stack<ICodeMetaInfo>()
|
||||
|
||||
for (i in originalText.indices) {
|
||||
val c = originalText[i]
|
||||
var prev: ICodeMetaInfo? = null
|
||||
|
||||
while (!opened.isEmpty() && i == opened.peek().end) {
|
||||
if (prev == null || prev.start != opened.peek().start)
|
||||
closeString(result)
|
||||
prev = opened.pop()
|
||||
}
|
||||
if (sortedMetaInfos.any { it.start == i }) {
|
||||
openStartTag(result)
|
||||
val matchedCodeMetaInfos = sortedMetaInfos.filter { it.start == i }.toMutableList()
|
||||
val iterator = matchedCodeMetaInfos.listIterator()
|
||||
var current: ICodeMetaInfo? = iterator.next()
|
||||
|
||||
while (current != null) {
|
||||
val next: ICodeMetaInfo? = if (iterator.hasNext()) iterator.next() else null
|
||||
opened.push(current)
|
||||
result.append(current.asString())
|
||||
when {
|
||||
next == null ->
|
||||
closeStartTag(result)
|
||||
next.end == current.end ->
|
||||
result.append(", ")
|
||||
else ->
|
||||
closeStartAndOpenNewTag(result)
|
||||
}
|
||||
current = next
|
||||
}
|
||||
}
|
||||
result.append(c)
|
||||
}
|
||||
var prev: ICodeMetaInfo? = null
|
||||
|
||||
while (!opened.isEmpty() && originalText.length == opened.peek().end) {
|
||||
if (prev == null || prev.start != opened.peek().start)
|
||||
closeString(result)
|
||||
prev = opened.pop()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getSortedCodeMetaInfos(
|
||||
metaInfos: Collection<ICodeMetaInfo>,
|
||||
): MutableList<ICodeMetaInfo> {
|
||||
val result = metaInfos.toMutableList()
|
||||
result.sortWith(Comparator { d1: ICodeMetaInfo, d2: ICodeMetaInfo ->
|
||||
if (d1.start != d2.start) d1.start - d2.start else d2.end - d1.end
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
private fun closeString(result: StringBuffer) = result.append("<!>")
|
||||
private fun openStartTag(result: StringBuffer) = result.append("<!")
|
||||
private fun closeStartTag(result: StringBuffer) = result.append("!>")
|
||||
private fun closeStartAndOpenNewTag(result: StringBuffer) = result.append("!><!")
|
||||
}
|
||||
|
||||
fun clearFileFromDiagnosticMarkup(file: File) {
|
||||
val text = file.readText()
|
||||
val cleanText = clearTextFromDiagnosticMarkup(text)
|
||||
file.writeText(cleanText)
|
||||
}
|
||||
|
||||
fun clearTextFromDiagnosticMarkup(text: String): String = CheckerTestUtil.rangeStartOrEndPattern.matcher(text).replaceAll("")
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.codeMetaInfo.models
|
||||
|
||||
import com.intellij.codeInsight.daemon.LineMarkerInfo
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import org.jetbrains.kotlin.checkers.diagnostics.ActualDiagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.AbstractCodeMetaInfoRenderConfiguration
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.DiagnosticCodeMetaInfoRenderConfiguration
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.HighlightingRenderConfiguration
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.LineMarkerRenderConfiguration
|
||||
import org.jetbrains.kotlin.idea.editor.fixers.end
|
||||
import org.jetbrains.kotlin.idea.editor.fixers.start
|
||||
|
||||
interface ICodeMetaInfo {
|
||||
|
||||
val start: Int
|
||||
val end: Int
|
||||
val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?
|
||||
|
||||
fun asString(): String
|
||||
}
|
||||
|
||||
class DiagnosticCodeMetaInfo(
|
||||
override val start: Int,
|
||||
override val end: Int,
|
||||
override val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?,
|
||||
val diagnostic: Diagnostic
|
||||
) :
|
||||
ICodeMetaInfo {
|
||||
|
||||
override fun asString(): String {
|
||||
return renderConfiguration!!.asString(this)
|
||||
}
|
||||
}
|
||||
|
||||
class LineMarkerCodeMetaInfo(
|
||||
override val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?,
|
||||
val lineMarker: LineMarkerInfo<*>
|
||||
) : ICodeMetaInfo {
|
||||
|
||||
override val start: Int
|
||||
get() = lineMarker.startOffset
|
||||
override val end: Int
|
||||
get() = lineMarker.endOffset
|
||||
|
||||
val withDescription = true
|
||||
|
||||
override fun asString(): String {
|
||||
return renderConfiguration!!.asString(this)
|
||||
}
|
||||
}
|
||||
|
||||
class HighlightingCodeMetaInfo(
|
||||
override val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?,
|
||||
val highlightingInfo: HighlightInfo
|
||||
) : ICodeMetaInfo {
|
||||
|
||||
override val start: Int
|
||||
get() = highlightingInfo.startOffset
|
||||
override val end: Int
|
||||
get() = highlightingInfo.endOffset
|
||||
|
||||
override fun asString(): String {
|
||||
return renderConfiguration!!.asString(this)
|
||||
}
|
||||
}
|
||||
|
||||
object CodeMetaInfoFactory {
|
||||
private fun createCodeMetaInfo(obj: Any, renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?): Collection<ICodeMetaInfo> {
|
||||
return when (obj) {
|
||||
is Diagnostic -> {
|
||||
if (renderConfiguration != null && renderConfiguration !is DiagnosticCodeMetaInfoRenderConfiguration)
|
||||
throw IllegalArgumentException("Unexpected render configuration for CodeMetaInfo object $obj")
|
||||
obj.textRanges.map { DiagnosticCodeMetaInfo(it.start, it.end, renderConfiguration, obj) }
|
||||
}
|
||||
is ActualDiagnostic -> {
|
||||
if (renderConfiguration != null && renderConfiguration !is DiagnosticCodeMetaInfoRenderConfiguration)
|
||||
throw IllegalArgumentException("Unexpected render configuration for CodeMetaInfo object $obj")
|
||||
obj.diagnostic.textRanges.map { DiagnosticCodeMetaInfo(it.start, it.end, renderConfiguration, obj.diagnostic) }
|
||||
}
|
||||
is HighlightInfo -> {
|
||||
if (renderConfiguration != null && renderConfiguration !is HighlightingRenderConfiguration)
|
||||
throw IllegalArgumentException("Unexpected render configuration for CodeMetaInfo object $obj")
|
||||
listOf(HighlightingCodeMetaInfo(renderConfiguration, obj))
|
||||
}
|
||||
is LineMarkerInfo<*> -> {
|
||||
if (renderConfiguration != null && renderConfiguration !is LineMarkerRenderConfiguration)
|
||||
throw IllegalArgumentException("Unexpected render configuration for CodeMetaInfo object $obj")
|
||||
listOf(LineMarkerCodeMetaInfo(renderConfiguration, obj))
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown type for creating CodeMetaInfo object $obj")
|
||||
}
|
||||
}
|
||||
|
||||
fun getCodeMetaInfo(
|
||||
objects: Collection<Any>,
|
||||
renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?
|
||||
): Collection<ICodeMetaInfo> {
|
||||
return objects.flatMap { createCodeMetaInfo(it, renderConfiguration) }
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.codeMetaInfo.renderConfigurations
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.lang.annotation.HighlightSeverity
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.*
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.models.DiagnosticCodeMetaInfo
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.models.HighlightingCodeMetaInfo
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.models.ICodeMetaInfo
|
||||
import org.jetbrains.kotlin.idea.codeMetaInfo.models.LineMarkerCodeMetaInfo
|
||||
|
||||
|
||||
abstract class AbstractCodeMetaInfoRenderConfiguration(var renderParams: Boolean = true) {
|
||||
|
||||
abstract fun asString(codeMetaInfo: ICodeMetaInfo): String
|
||||
|
||||
open fun getAdditionalTags(codeMetaInfo: ICodeMetaInfo) = ""
|
||||
|
||||
protected fun sanitizeLineMarkerTooltip(originalText: String?): String {
|
||||
if (originalText == null) return "null"
|
||||
val noHtmlTags = StringUtil.removeHtmlTags(originalText)
|
||||
return sanitizeLineBreaks(noHtmlTags)
|
||||
}
|
||||
|
||||
protected fun sanitizeLineBreaks(originalText: String): String {
|
||||
return StringUtil.replace(originalText, "\n", " ")
|
||||
}
|
||||
}
|
||||
|
||||
open class DiagnosticCodeMetaInfoRenderConfiguration(
|
||||
val withNewInference: Boolean = true,
|
||||
val renderSeverity: Boolean = false
|
||||
) : AbstractCodeMetaInfoRenderConfiguration() {
|
||||
|
||||
override fun asString(codeMetaInfo: ICodeMetaInfo): String {
|
||||
if (codeMetaInfo !is DiagnosticCodeMetaInfo) return ""
|
||||
return (getTag(codeMetaInfo) + if (renderParams) "(\"${getParamsString(codeMetaInfo)}\")" else "")
|
||||
.replace(Regex("""\r?\n"""), "")
|
||||
}
|
||||
|
||||
private fun getParamsString(codeMetaInfo: DiagnosticCodeMetaInfo): String {
|
||||
val params = mutableListOf<String>()
|
||||
val renderer = when (codeMetaInfo.diagnostic.factory) {
|
||||
is DebugInfoDiagnosticFactory1 -> DiagnosticWithParameters1Renderer(
|
||||
"{0}",
|
||||
Renderers.TO_STRING
|
||||
) as DiagnosticRenderer<Diagnostic>
|
||||
else -> DefaultErrorMessages.getRendererForDiagnostic(codeMetaInfo.diagnostic)
|
||||
}
|
||||
if (renderer is AbstractDiagnosticWithParametersRenderer) {
|
||||
val renderParameters = renderer.renderParameters(codeMetaInfo.diagnostic)
|
||||
params.addAll(ContainerUtil.map(renderParameters) { it.toString() })
|
||||
}
|
||||
if (renderSeverity)
|
||||
params.add("severity='${codeMetaInfo.diagnostic.severity}'")
|
||||
|
||||
params.add(getAdditionalTags(codeMetaInfo))
|
||||
|
||||
return params.filter { it.isNotEmpty() }.joinToString("; ")
|
||||
}
|
||||
|
||||
private fun getTag(codeMetaInfo: DiagnosticCodeMetaInfo): String {
|
||||
return codeMetaInfo.diagnostic.factory.name
|
||||
}
|
||||
}
|
||||
|
||||
open class LineMarkerRenderConfiguration(val renderDescription: Boolean = true) : AbstractCodeMetaInfoRenderConfiguration() {
|
||||
override fun asString(codeMetaInfo: ICodeMetaInfo): String {
|
||||
if (codeMetaInfo !is LineMarkerCodeMetaInfo) return ""
|
||||
return getTag() + if (renderParams) "(\"${getParamsString(codeMetaInfo)}\")" else ""
|
||||
}
|
||||
|
||||
private fun getTag(): String {
|
||||
return "LINE_MARKER"
|
||||
}
|
||||
|
||||
private fun getParamsString(lineMarkerCodeMetaInfo: LineMarkerCodeMetaInfo): String {
|
||||
val params = mutableListOf<String>()
|
||||
|
||||
if (renderDescription)
|
||||
params.add("descr='${sanitizeLineMarkerTooltip(lineMarkerCodeMetaInfo.lineMarker.lineMarkerTooltip)}'")
|
||||
|
||||
params.add(getAdditionalTags(lineMarkerCodeMetaInfo))
|
||||
|
||||
return params.filter { it.isNotEmpty() }.joinToString("; ")
|
||||
}
|
||||
}
|
||||
|
||||
open class HighlightingRenderConfiguration(
|
||||
val renderDescription: Boolean = true,
|
||||
val renderTextAttributesKey: Boolean = true,
|
||||
val renderSeverity: Boolean = true
|
||||
) : AbstractCodeMetaInfoRenderConfiguration() {
|
||||
|
||||
override fun asString(codeMetaInfo: ICodeMetaInfo): String {
|
||||
if (codeMetaInfo !is HighlightingCodeMetaInfo) return ""
|
||||
return getTag() + if (renderParams) "(${getParamsString(codeMetaInfo)})" else ""
|
||||
}
|
||||
|
||||
private fun getTag(): String {
|
||||
return "HIGHLIGHTING"
|
||||
}
|
||||
|
||||
private fun getParamsString(highlightingCodeMetaInfo: HighlightingCodeMetaInfo): String {
|
||||
val params = mutableListOf<String>()
|
||||
|
||||
if (renderSeverity)
|
||||
params.add("severity='${getSeverity(highlightingCodeMetaInfo.highlightingInfo)}'")
|
||||
if (renderDescription)
|
||||
params.add("descr='${sanitizeLineBreaks(highlightingCodeMetaInfo.highlightingInfo.description)}'")
|
||||
if (renderTextAttributesKey)
|
||||
params.add("textAttributesKey='${highlightingCodeMetaInfo.highlightingInfo.forcedTextAttributesKey}'")
|
||||
|
||||
params.add(getAdditionalTags(highlightingCodeMetaInfo))
|
||||
|
||||
return params.filter { it.isNotEmpty() }.joinToString("; ")
|
||||
}
|
||||
|
||||
private fun getSeverity(highlightingInfo: HighlightInfo): String {
|
||||
return if (highlightingInfo.severity == HighlightSeverity.INFORMATION) "info" else highlightingInfo.severity.toString()
|
||||
.toLowerCase()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user