Added support for various platforms for CodeMetaInfo tests. ^KT-41996

The tests need to be able to parse and render CodeMetaInfo for different
 platforms. For example, we must draw a run gutter a MAC application on
 OSX, but for a Win application, the gutter should not be drawn at the
 same time. This commit allows to do such checks. In order to indicate
 on which platform it should be rendered, it is necessary to specify the
 platform for a specific CodeMetaInfo in the test data in the format
 {OSX,Unix,Win}, if the block with platforms is missing, this means
 that it should be rendered on each platform.

<!LINE_MARKER{OSX}(...)!> - should only be on OSX
<!LINE_MARKER(...)!> - should be on any platform
This commit is contained in:
Alexander Dudinsky
2020-10-10 10:05:03 +03:00
parent 84d24e5b76
commit 94bf38a7b5
6 changed files with 226 additions and 140 deletions
@@ -60,7 +60,7 @@ object CheckerTestUtil {
const val OLD_INFERENCE_PREFIX = "OI"
private const val IGNORE_DIAGNOSTIC_PARAMETER = "IGNORE"
private const val INDIVIDUAL_DIAGNOSTIC = """(\w+;)?(\w+:)?(\w+)(?:\(((?:".*?")(?:,\s*".*?")*)\))?"""
private const val INDIVIDUAL_DIAGNOSTIC = """(\w+;)?(\w+:)?(\w+)(\{[\w;]+})?(?:\(((?:".*?")(?:,\s*".*?")*)\))?"""
val rangeStartOrEndPattern = Pattern.compile("(<!$INDIVIDUAL_DIAGNOSTIC(,\\s*$INDIVIDUAL_DIAGNOSTIC)*!>)|(<!>)")
val individualDiagnosticPattern: Pattern = Pattern.compile(INDIVIDUAL_DIAGNOSTIC)
@@ -32,13 +32,12 @@ 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.daemon.common.OSKind
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.diagnostics.AbstractDiagnostic
import org.jetbrains.kotlin.diagnostics.Severity
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.models.*
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.AbstractCodeMetaInfoRenderConfiguration
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.DiagnosticCodeMetaInfoRenderConfiguration
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.HighlightingRenderConfiguration
@@ -53,12 +52,15 @@ import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
import java.nio.file.Paths
class CodeMetaInfoTestCase(val codeMetaInfoTypes: Collection<AbstractCodeMetaInfoRenderConfiguration>) : DaemonAnalyzerTestCase() {
class CodeMetaInfoTestCase(
val codeMetaInfoTypes: Collection<AbstractCodeMetaInfoRenderConfiguration>,
val checkNoDiagnosticError: Boolean = false
) : DaemonAnalyzerTestCase() {
fun getDiagnosticCodeMetaInfos(
configuration: DiagnosticCodeMetaInfoRenderConfiguration?,
configuration: DiagnosticCodeMetaInfoRenderConfiguration = DiagnosticCodeMetaInfoRenderConfiguration(),
parseDirective: Boolean = true
): Collection<ICodeMetaInfo> {
): List<CodeMetaInfo> {
val tempSourceKtFile = PsiManager.getInstance(project).findFile(file.virtualFile) as KtFile
val resolutionFacade = tempSourceKtFile.getResolutionFacade()
val (bindingContext, moduleDescriptor) = resolutionFacade.analyzeWithAllCompilerChecks(listOf(tempSourceKtFile))
@@ -77,23 +79,23 @@ class CodeMetaInfoTestCase(val codeMetaInfoTypes: Collection<AbstractCodeMetaInf
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)
configuration.renderParams = directives.contains(BaseDiagnosticsTest.RENDER_DIAGNOSTICS_MESSAGES)
return getCodeMetaInfo(diagnostics, configuration)
}
fun getLineMarkerCodeMetaInfos(configuration: LineMarkerRenderConfiguration): Collection<ICodeMetaInfo> {
fun getLineMarkerCodeMetaInfos(configuration: LineMarkerRenderConfiguration): Collection<CodeMetaInfo> {
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)
return getCodeMetaInfo(lineMarkers, configuration)
}
fun getHighlightingCodeMetaInfos(configuration: HighlightingRenderConfiguration?): Collection<ICodeMetaInfo> {
fun getHighlightingCodeMetaInfos(configuration: HighlightingRenderConfiguration): Collection<CodeMetaInfo> {
val infos = CodeInsightTestFixtureImpl.instantiateAndRun(file, editor, TIntArrayList().toNativeArray(), false)
return CodeMetaInfoFactory.getCodeMetaInfo(infos, configuration)
return getCodeMetaInfo(infos, configuration)
}
fun checkFile(expectedFile: File, project: Project, editor: Editor) {
@@ -113,8 +115,7 @@ class CodeMetaInfoTestCase(val codeMetaInfoTypes: Collection<AbstractCodeMetaInf
}
fun check(expectedFile: File) {
val codeMetaInfoForCheck = mutableListOf<ICodeMetaInfo>()
val codeMetaInfoForCheck = mutableListOf<CodeMetaInfo>()
PsiDocumentManager.getInstance(myProject).commitAllDocuments()
//to load text
@@ -144,22 +145,46 @@ class CodeMetaInfoTestCase(val codeMetaInfoTypes: Collection<AbstractCodeMetaInf
!codeMetaInfoTypes.any { it is HighlightingRenderConfiguration }
) {
checkHighlightErrorItemsInDiagnostics(
getDiagnosticCodeMetaInfos(null, false).filterIsInstance<DiagnosticCodeMetaInfo>()
getDiagnosticCodeMetaInfos(DiagnosticCodeMetaInfoRenderConfiguration(), false).filterIsInstance<DiagnosticCodeMetaInfo>()
)
}
val parsedMetaInfo = CodeMetaInfoParser.getCodeMetaInfoFromText(expectedFile.readText()).toMutableList()
codeMetaInfoForCheck.forEach { codeMetaInfo ->
val correspondingParsed = parsedMetaInfo.firstOrNull { it == codeMetaInfo }
if (correspondingParsed != null) {
parsedMetaInfo.remove(correspondingParsed)
codeMetaInfo.platforms.addAll(correspondingParsed.platforms)
if (correspondingParsed.platforms.isNotEmpty() && OSKind.current.toString() !in correspondingParsed.platforms)
codeMetaInfo.platforms.add(OSKind.current.toString())
}
}
if (parsedMetaInfo.isNotEmpty())
parsedMetaInfo.forEach {
if (it.platforms.isNotEmpty() && OSKind.current.toString() !in it.platforms) codeMetaInfoForCheck.add(
it
)
}
val textWithCodeMetaInfo = CodeMetaInfoRenderer.renderTagsToText(codeMetaInfoForCheck, myEditor.document.text)
KotlinTestUtils.assertEqualsToFile(
expectedFile,
textWithCodeMetaInfo.toString()
)
if (checkNoDiagnosticError) {
val diagnosticsErrors =
getDiagnosticCodeMetaInfos().filter { (it as DiagnosticCodeMetaInfo).diagnostic.severity == Severity.ERROR }
assertTrue(
"Diagnostics with severity ERROR were found: ${diagnosticsErrors.joinToString { it.asString() }}",
diagnosticsErrors.isEmpty()
)
}
}
private fun checkHighlightErrorItemsInDiagnostics(
diagnostics: Collection<DiagnosticCodeMetaInfo>
) {
val highlightItems: List<ICodeMetaInfo> =
getHighlightingCodeMetaInfos(null).filter { (it as HighlightingCodeMetaInfo).highlightingInfo.severity == HighlightSeverity.ERROR }
val highlightItems: List<CodeMetaInfo> =
getHighlightingCodeMetaInfos(HighlightingRenderConfiguration()).filter { (it as HighlightingCodeMetaInfo).highlightingInfo.severity == HighlightSeverity.ERROR }
highlightItems.forEach { highlightingCodeMetaInfo ->
assert(
@@ -201,7 +226,7 @@ abstract class AbstractHighlightingCodeMetaInfoTest : AbstractCodeMetaInfoTest()
}
abstract class AbstractCodeMetaInfoTest : AbstractMultiModuleTest() {
open val checkNoDiagnosticError get() = false
open fun getConfigurations() = listOf(
DiagnosticCodeMetaInfoRenderConfiguration(),
LineMarkerRenderConfiguration(),
@@ -218,7 +243,7 @@ abstract class AbstractCodeMetaInfoTest : AbstractMultiModuleTest() {
fun doTest(testDataPath: String) {
val testRoot = File(testDataPath)
val checker = CodeMetaInfoTestCase(getConfigurations())
val checker = CodeMetaInfoTestCase(getConfigurations(), checkNoDiagnosticError)
setupProject(testDataPath)
for (module in ModuleManager.getInstance(project).modules) {
@@ -237,16 +262,13 @@ abstract class AbstractCodeMetaInfoTest : AbstractMultiModuleTest() {
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,65 @@
/*
* 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.idea.codeMetaInfo.models.ParsedCodeMetaInfo
import org.junit.Assert
object CodeMetaInfoParser {
private val openingRegex = "(<!([^>]+?)!>)".toRegex()
private val closingRegex = "(<!>)".toRegex()
private val descriptionRegex = "\\(\".*?\"\\)".toRegex()
private val platformRegex = "\\{(.+)}".toRegex()
fun getCodeMetaInfoFromText(renderedText: String): List<ParsedCodeMetaInfo> {
var text = renderedText
val openingMatchResults = Stack<MatchResult>()
val closingMatchResults = Stack<MatchResult>()
val result = mutableListOf<ParsedCodeMetaInfo>()
while (true) {
var openingStartOffset = Int.MAX_VALUE
var closingStartOffset = Int.MAX_VALUE
val opening = openingRegex.find(text)
val closing = closingRegex.find(text)
if (opening == null && closing == null) break
if (opening != null)
openingStartOffset = opening.range.first
if (closing != null)
closingStartOffset = closing.range.first
text = if (openingStartOffset < closingStartOffset) {
openingMatchResults.push(opening)
text.removeRange(openingStartOffset, opening!!.range.last + 1)
} else {
closingMatchResults.push(closing)
text.removeRange(closingStartOffset, closing!!.range.last + 1)
}
}
if (openingMatchResults.size != closingMatchResults.size) {
Assert.fail("Opening and closing tags counts are not equals")
}
while (!openingMatchResults.isEmpty()) {
val openingMatchResult = openingMatchResults.pop()
val closingMatchResult = closingMatchResults.pop()
val metaInfoWithoutParams = openingMatchResult.groups[2]!!.value.replace(descriptionRegex, "")
metaInfoWithoutParams.split(",").forEach {
val tag = platformRegex.replace(it, "").trim()
val platforms =
if (platformRegex.containsMatchIn(it)) platformRegex.find(it)!!.destructured.component1().split(";") else listOf()
result.add(
ParsedCodeMetaInfo(
openingMatchResult.range.first, closingMatchResult.range.first, platforms.toMutableList(), tag,
)
)
}
}
return result
}
}
@@ -7,13 +7,12 @@ 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 org.jetbrains.kotlin.idea.codeMetaInfo.models.CodeMetaInfo
import java.io.File
object CodeMetaInfoRenderer {
fun renderTagsToText(
codeMetaInfos: List<ICodeMetaInfo>,
codeMetaInfos: List<CodeMetaInfo>,
originalText: String
): StringBuffer {
val result = StringBuffer()
@@ -22,25 +21,18 @@ object CodeMetaInfoRenderer {
return result
}
val sortedMetaInfos = getSortedCodeMetaInfos(codeMetaInfos)
val opened = Stack<ICodeMetaInfo>()
val opened = Stack<CodeMetaInfo>()
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 }) {
for ((i, c) in originalText.withIndex()) {
checkOpenedAndCloseStringIfNeeded(opened, i, result)
val matchedCodeMetaInfos = sortedMetaInfos.filter { it.start == i }
if (matchedCodeMetaInfos.isNotEmpty()) {
openStartTag(result)
val matchedCodeMetaInfos = sortedMetaInfos.filter { it.start == i }.toMutableList()
val iterator = matchedCodeMetaInfos.listIterator()
var current: ICodeMetaInfo? = iterator.next()
var current: CodeMetaInfo? = iterator.next()
while (current != null) {
val next: ICodeMetaInfo? = if (iterator.hasNext()) iterator.next() else null
val next: CodeMetaInfo? = if (iterator.hasNext()) iterator.next() else null
opened.push(current)
result.append(current.asString())
when {
@@ -56,30 +48,27 @@ object CodeMetaInfoRenderer {
}
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()
}
checkOpenedAndCloseStringIfNeeded(opened, originalText.length, result)
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 getSortedCodeMetaInfos(metaInfos: Collection<CodeMetaInfo>): List<CodeMetaInfo> {
return metaInfos.sortedWith(compareBy<CodeMetaInfo> { it.start }.then(compareByDescending { it.end }))
}
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("!><!")
private fun checkOpenedAndCloseStringIfNeeded(opened: Stack<CodeMetaInfo>, end: Int, result: StringBuffer) {
var prev: CodeMetaInfo? = null
while (!opened.isEmpty() && end == opened.peek().end) {
if (prev == null || prev.start != opened.peek().start)
closeString(result)
prev = opened.pop()
}
}
}
fun clearFileFromDiagnosticMarkup(file: File) {
@@ -16,91 +16,103 @@ import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.LineMarkerRen
import org.jetbrains.kotlin.idea.editor.fixers.end
import org.jetbrains.kotlin.idea.editor.fixers.start
interface ICodeMetaInfo {
interface CodeMetaInfo {
val start: Int
val end: Int
val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?
val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration
val platforms: MutableList<String>
fun asString(): String
fun getTag(): String
}
class DiagnosticCodeMetaInfo(
override val start: Int,
override val end: Int,
override val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?,
override val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration,
val diagnostic: Diagnostic
) :
ICodeMetaInfo {
) : CodeMetaInfo {
override val platforms: MutableList<String> = mutableListOf()
override fun asString(): String {
return renderConfiguration!!.asString(this)
}
override fun asString() = renderConfiguration.asString(this)
override fun getTag() = (renderConfiguration as DiagnosticCodeMetaInfoRenderConfiguration).getTag(this)
}
class LineMarkerCodeMetaInfo(
override val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?,
override val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration,
val lineMarker: LineMarkerInfo<*>
) : ICodeMetaInfo {
) : CodeMetaInfo {
override val start: Int
get() = lineMarker.startOffset
override val end: Int
get() = lineMarker.endOffset
override val platforms: MutableList<String> = mutableListOf()
val withDescription = true
override fun asString() = renderConfiguration.asString(this)
override fun asString(): String {
return renderConfiguration!!.asString(this)
}
override fun getTag() = (renderConfiguration as LineMarkerRenderConfiguration).getTag()
}
class HighlightingCodeMetaInfo(
override val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration?,
override val renderConfiguration: AbstractCodeMetaInfoRenderConfiguration,
val highlightingInfo: HighlightInfo
) : ICodeMetaInfo {
) : CodeMetaInfo {
override val start: Int
get() = highlightingInfo.startOffset
override val end: Int
get() = highlightingInfo.endOffset
override val platforms: MutableList<String> = mutableListOf()
override fun asString(): String {
return renderConfiguration!!.asString(this)
override fun asString() = renderConfiguration.asString(this)
override fun getTag() = (renderConfiguration as HighlightingRenderConfiguration).getTag()
}
class ParsedCodeMetaInfo(
override val start: Int,
override val end: Int,
override val platforms: MutableList<String>,
private val tag: String
) : CodeMetaInfo {
override val renderConfiguration = object : AbstractCodeMetaInfoRenderConfiguration(false) {}
override fun asString() = renderConfiguration.asString(this)
override fun equals(other: Any?): Boolean {
if (other == null || other !is CodeMetaInfo) return false
return this.tag == other.getTag() && this.start == other.start && this.end == other.end
}
override fun getTag() = tag
}
fun createCodeMetaInfo(obj: Any, renderConfiguration: AbstractCodeMetaInfoRenderConfiguration): List<CodeMetaInfo> {
fun errorMessage() = "Unexpected render configuration for object $obj"
return when (obj) {
is Diagnostic -> {
require(renderConfiguration is DiagnosticCodeMetaInfoRenderConfiguration, ::errorMessage)
obj.textRanges.map { DiagnosticCodeMetaInfo(it.start, it.end, renderConfiguration, obj) }
}
is ActualDiagnostic -> {
require(renderConfiguration is DiagnosticCodeMetaInfoRenderConfiguration, ::errorMessage)
obj.diagnostic.textRanges.map { DiagnosticCodeMetaInfo(it.start, it.end, renderConfiguration, obj.diagnostic) }
}
is HighlightInfo -> {
require(renderConfiguration is HighlightingRenderConfiguration, ::errorMessage)
listOf(HighlightingCodeMetaInfo(renderConfiguration, obj))
}
is LineMarkerInfo<*> -> {
require(renderConfiguration is LineMarkerRenderConfiguration, ::errorMessage)
listOf(LineMarkerCodeMetaInfo(renderConfiguration, obj))
}
else -> throw IllegalArgumentException("Unknown type for creating CodeMetaInfo object $obj")
}
}
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) }
}
}
fun getCodeMetaInfo(
objects: List<Any>,
renderConfiguration: AbstractCodeMetaInfoRenderConfiguration
): List<CodeMetaInfo> {
return objects.flatMap { createCodeMetaInfo(it, renderConfiguration) }
}
@@ -5,8 +5,6 @@
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
@@ -14,39 +12,48 @@ 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.CodeMetaInfo
import org.jetbrains.kotlin.idea.codeMetaInfo.models.LineMarkerCodeMetaInfo
abstract class AbstractCodeMetaInfoRenderConfiguration(var renderParams: Boolean = true) {
abstract fun asString(codeMetaInfo: ICodeMetaInfo): String
open fun asString(codeMetaInfo: CodeMetaInfo) = codeMetaInfo.getTag() + getPlatformsString(codeMetaInfo)
open fun getAdditionalTags(codeMetaInfo: ICodeMetaInfo) = ""
open fun getAdditionalParams(codeMetaInfo: CodeMetaInfo) = ""
protected fun sanitizeLineMarkerTooltip(originalText: String?): String {
if (originalText == null) return "null"
val noHtmlTags = StringUtil.removeHtmlTags(originalText)
val noHtmlTags = StringUtil.removeHtmlTags(originalText).replace(" ", "")
return sanitizeLineBreaks(noHtmlTags)
}
protected fun sanitizeLineBreaks(originalText: String): String {
return StringUtil.replace(originalText, "\n", " ")
}
protected fun getPlatformsString(codeMetaInfo: CodeMetaInfo): String {
if (codeMetaInfo.platforms.isEmpty()) return ""
return "{${codeMetaInfo.platforms.joinToString(";")}}"
}
}
open class DiagnosticCodeMetaInfoRenderConfiguration(
val withNewInference: Boolean = true,
val renderSeverity: Boolean = false
) : AbstractCodeMetaInfoRenderConfiguration() {
private val crossPlatformLineBreak = """\r?\n""".toRegex()
override fun asString(codeMetaInfo: ICodeMetaInfo): String {
override fun asString(codeMetaInfo: CodeMetaInfo): String {
if (codeMetaInfo !is DiagnosticCodeMetaInfo) return ""
return (getTag(codeMetaInfo) + if (renderParams) "(\"${getParamsString(codeMetaInfo)}\")" else "")
.replace(Regex("""\r?\n"""), "")
return (getTag(codeMetaInfo)
+ getPlatformsString(codeMetaInfo)
+ getParamsString(codeMetaInfo))
.replace(crossPlatformLineBreak, "")
}
private fun getParamsString(codeMetaInfo: DiagnosticCodeMetaInfo): String {
if (!renderParams) return ""
val params = mutableListOf<String>()
val renderer = when (codeMetaInfo.diagnostic.factory) {
is DebugInfoDiagnosticFactory1 -> DiagnosticWithParameters1Renderer(
@@ -62,25 +69,23 @@ open class DiagnosticCodeMetaInfoRenderConfiguration(
if (renderSeverity)
params.add("severity='${codeMetaInfo.diagnostic.severity}'")
params.add(getAdditionalTags(codeMetaInfo))
params.add(getAdditionalParams(codeMetaInfo))
return params.filter { it.isNotEmpty() }.joinToString("; ")
return "(\"${params.filter { it.isNotEmpty() }.joinToString("; ")}\")"
}
private fun getTag(codeMetaInfo: DiagnosticCodeMetaInfo): String {
fun getTag(codeMetaInfo: DiagnosticCodeMetaInfo): String {
return codeMetaInfo.diagnostic.factory.name
}
}
open class LineMarkerRenderConfiguration(val renderDescription: Boolean = true) : AbstractCodeMetaInfoRenderConfiguration() {
override fun asString(codeMetaInfo: ICodeMetaInfo): String {
override fun asString(codeMetaInfo: CodeMetaInfo): String {
if (codeMetaInfo !is LineMarkerCodeMetaInfo) return ""
return getTag() + if (renderParams) "(\"${getParamsString(codeMetaInfo)}\")" else ""
}
private fun getTag(): String {
return "LINE_MARKER"
}
fun getTag() = "LINE_MARKER"
private fun getParamsString(lineMarkerCodeMetaInfo: LineMarkerCodeMetaInfo): String {
val params = mutableListOf<String>()
@@ -88,7 +93,7 @@ open class LineMarkerRenderConfiguration(val renderDescription: Boolean = true)
if (renderDescription)
params.add("descr='${sanitizeLineMarkerTooltip(lineMarkerCodeMetaInfo.lineMarker.lineMarkerTooltip)}'")
params.add(getAdditionalTags(lineMarkerCodeMetaInfo))
params.add(getAdditionalParams(lineMarkerCodeMetaInfo))
return params.filter { it.isNotEmpty() }.joinToString("; ")
}
@@ -100,32 +105,25 @@ open class HighlightingRenderConfiguration(
val renderSeverity: Boolean = true
) : AbstractCodeMetaInfoRenderConfiguration() {
override fun asString(codeMetaInfo: ICodeMetaInfo): String {
override fun asString(codeMetaInfo: CodeMetaInfo): String {
if (codeMetaInfo !is HighlightingCodeMetaInfo) return ""
return getTag() + if (renderParams) "(${getParamsString(codeMetaInfo)})" else ""
}
private fun getTag(): String {
return "HIGHLIGHTING"
}
fun getTag() = "HIGHLIGHTING"
private fun getParamsString(highlightingCodeMetaInfo: HighlightingCodeMetaInfo): String {
val params = mutableListOf<String>()
if (renderSeverity)
params.add("severity='${getSeverity(highlightingCodeMetaInfo.highlightingInfo)}'")
params.add("severity='${highlightingCodeMetaInfo.highlightingInfo.severity}'")
if (renderDescription)
params.add("descr='${sanitizeLineBreaks(highlightingCodeMetaInfo.highlightingInfo.description)}'")
if (renderTextAttributesKey)
params.add("textAttributesKey='${highlightingCodeMetaInfo.highlightingInfo.forcedTextAttributesKey}'")
params.add(getAdditionalTags(highlightingCodeMetaInfo))
params.add(getAdditionalParams(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()
}
}