From 9cb950a2aec52550334059a74d81bcbe1bfab3bc Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Mon, 29 Nov 2021 15:07:29 +0100 Subject: [PATCH] Analysis API: extract PrettyPrinter from DebugSymbolRenderer to utils --- analysis/analysis-api/build.gradle.kts | 1 + .../api/symbols/DebugSymbolRenderer.kt | 92 ++++----------- .../analysis/utils/printer/PrettyPrinter.kt | 105 ++++++++++++++++++ 3 files changed, 130 insertions(+), 68 deletions(-) create mode 100644 analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/printer/PrettyPrinter.kt diff --git a/analysis/analysis-api/build.gradle.kts b/analysis/analysis-api/build.gradle.kts index 294cd30312f..58b9c071830 100644 --- a/analysis/analysis-api/build.gradle.kts +++ b/analysis/analysis-api/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { compileOnly(project(":core:compiler.common")) compileOnly(project(":core:compiler.common.jvm")) compileOnly(project(":analysis:low-level-api-fir")) + implementation(project(":analysis:analysis-internal-utils")) implementation(project(":analysis:analysis-api-providers")) api(intellijCoreDep()) { includeJars("intellij-core", "guava", rootProject = rootProject) } diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt index 28ab9c26ae5..533ae15b599 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/DebugSymbolRenderer.kt @@ -16,9 +16,10 @@ import org.jetbrains.kotlin.analysis.api.components.KtSymbolInfoProviderMixIn import org.jetbrains.kotlin.analysis.api.symbols.markers.* import org.jetbrains.kotlin.analysis.api.types.KtClassErrorType import org.jetbrains.kotlin.analysis.api.types.KtType +import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter +import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.name.* -import java.lang.Appendable import java.lang.reflect.InvocationTargetException import kotlin.reflect.KClass import kotlin.reflect.KProperty @@ -26,14 +27,14 @@ import kotlin.reflect.full.declaredMemberExtensionProperties import kotlin.reflect.full.extensionReceiverParameter public object DebugSymbolRenderer { - public fun render(symbol: KtSymbol): String = Block().apply { renderSymbol(symbol) }.toString() + public fun render(symbol: KtSymbol): String = prettyPrint { renderSymbol(symbol) } public fun renderAnnotationApplication(application: KtAnnotationApplication): String = - Block().apply { renderAnnotationApplication(application) }.toString() + prettyPrint { renderAnnotationApplication(application) } - public fun renderType(type: KtType): String = Block().apply { renderType(type) }.toString() + public fun renderType(type: KtType): String = prettyPrint { renderType(type) } - public fun KtAnalysisSession.renderExtra(symbol: KtSymbol): String = Block().apply { + public fun KtAnalysisSession.renderExtra(symbol: KtSymbol): String = prettyPrint { renderSymbol(symbol) withIndent { @@ -48,10 +49,10 @@ public object DebugSymbolRenderer { .filter { (it.extensionReceiverParameter?.type?.classifier as? KClass<*>)?.isInstance(symbol) == true } .forEach { renderProperty(it, this@renderExtra, symbol) } } - }.toString() + } - public fun KtAnalysisSession.renderForSubstitutionOverrideUnwrappingTest(symbol: KtSymbol): String = Block().apply { - if (symbol !is KtCallableSymbol) return@apply + public fun KtAnalysisSession.renderForSubstitutionOverrideUnwrappingTest(symbol: KtSymbol): String = prettyPrint { + if (symbol !is KtCallableSymbol) return@prettyPrint renderSymbolHeader(symbol) @@ -63,14 +64,14 @@ public object DebugSymbolRenderer { renderProperty(KtCallableSymbol::origin, symbol) @Suppress("DEPRECATION") - (symbol as? KtPossibleMemberSymbol)?.getDispatchReceiverType()?.let { dispatchType -> + (symbol as? KtCallableSymbol)?.getDispatchReceiverType()?.let { dispatchType -> appendLine().append("getDispatchReceiver()").append(": ") renderType(dispatchType) } } }.toString() - private fun Block.renderProperty(property: KProperty<*>, vararg args: Any) { + private fun PrettyPrinter.renderProperty(property: KProperty<*>, vararg args: Any) { try { appendLine().append(property.name).append(": ") renderValue(property.getter.call(*args)) @@ -79,9 +80,8 @@ public object DebugSymbolRenderer { } } - private fun Block.renderSymbol(symbol: KtSymbol) { + private fun PrettyPrinter.renderSymbol(symbol: KtSymbol) { renderSymbolHeader(symbol) - val apiClass = getSymbolApiClass(symbol) withIndent { apiClass.members @@ -93,28 +93,26 @@ public object DebugSymbolRenderer { } } - private fun Block.renderSymbolHeader(symbol: KtSymbol) { + private fun PrettyPrinter.renderSymbolHeader(symbol: KtSymbol) { val apiClass = getSymbolApiClass(symbol) append(apiClass.simpleName).append(':') } - private fun Block.renderList(values: List<*>) { + private fun PrettyPrinter.renderList(values: List<*>) { if (values.isEmpty()) { append("[]") return } - append('[') - withIndent { + withIndentInSquareBrackets { for (value in values) { appendLine() renderValue(value) } } - appendLine().append(']') } - private fun Block.renderSymbolTag(symbol: KtSymbol) { + private fun PrettyPrinter.renderSymbolTag(symbol: KtSymbol) { fun renderId(id: Any?, symbol: KtSymbol) { if (id != null) { renderValue(id) @@ -138,16 +136,16 @@ public object DebugSymbolRenderer { append(")") } - private fun Block.renderAnnotationValue(value: KtAnnotationValue) { + private fun PrettyPrinter.renderAnnotationValue(value: KtAnnotationValue) { append(KtAnnotationValueRenderer.render(value)) } - private fun Block.renderNamedConstantValue(value: KtNamedAnnotationValue) { + private fun PrettyPrinter.renderNamedConstantValue(value: KtNamedAnnotationValue) { append(value.name).append(" = ") renderValue(value.expression) } - private fun Block.renderType(type: KtType) { + private fun PrettyPrinter.renderType(type: KtType) { if (type.annotations.isNotEmpty()) { renderList(type.annotations) append(' ') @@ -158,7 +156,7 @@ public object DebugSymbolRenderer { } } - private fun Block.renderAnnotationApplication(call: KtAnnotationApplication) { + private fun PrettyPrinter.renderAnnotationApplication(call: KtAnnotationApplication) { renderValue(call.classId) append('(') call.arguments.sortedBy { it.name }.forEachIndexed { index, value -> @@ -175,7 +173,7 @@ public object DebugSymbolRenderer { } } - private fun Block.renderDeprecationInfo(info: DeprecationInfo) { + private fun PrettyPrinter.renderDeprecationInfo(info: DeprecationInfo) { append("DeprecationInfo(") append("deprecationLevel=${info.deprecationLevel}, ") append("propagatesToOverrides=${info.propagatesToOverrides}, ") @@ -183,7 +181,7 @@ public object DebugSymbolRenderer { append(")") } - private fun Block.renderValue(value: Any?) { + private fun PrettyPrinter.renderValue(value: Any?) { when (value) { // Symbol-related values is KtSymbol -> renderSymbolTag(value) @@ -211,7 +209,7 @@ public object DebugSymbolRenderer { } } - private fun Block.renderKtInitializerValue(value: KtInitializerValue) { + private fun PrettyPrinter.renderKtInitializerValue(value: KtInitializerValue) { when (value) { is KtConstantInitializerValue -> { append("KtConstantInitializerValue(") @@ -226,7 +224,7 @@ public object DebugSymbolRenderer { } } - private fun Block.renderAnnotationsList(value: KtAnnotationsList) { + private fun PrettyPrinter.renderAnnotationsList(value: KtAnnotationsList) { renderList(value.annotations) } @@ -255,46 +253,4 @@ public object DebugSymbolRenderer { "org.jetbrains.kotlin.analysis.api.fir", "org.jetbrains.kotlin.analysis.api.descriptors", ) -} - -private class Block : Appendable { - private val builder = StringBuilder() - private var indent = 0 - - override fun append(seq: CharSequence): Appendable = apply { - seq.split('\n').forEachIndexed { index, line -> - if (index > 0) { - builder.append('\n') - } - appendIndentIfNeeded() - builder.append(line) - } - } - - override fun append(seq: CharSequence, start: Int, end: Int): Appendable = apply { - append(seq.subSequence(start, end)) - } - - override fun append(c: Char): Appendable = apply { - if (c != '\n') { - appendIndentIfNeeded() - } - builder.append(c) - } - - private fun appendIndentIfNeeded() { - if (builder.isEmpty() || builder[builder.lastIndex] == '\n') { - builder.append(" ".repeat(2 * indent)) - } - } - - fun withIndent(block: Block.() -> Unit) { - indent += 1 - block(this) - indent -= 1 - } - - override fun toString(): String { - return builder.toString() - } } \ No newline at end of file diff --git a/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/printer/PrettyPrinter.kt b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/printer/PrettyPrinter.kt new file mode 100644 index 00000000000..df866ac6432 --- /dev/null +++ b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/printer/PrettyPrinter.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.utils.printer + +import java.lang.Appendable + +public class PrettyPrinter(private val indentSize: Int = 2) : Appendable { + private val builder = StringBuilder() + public var indent: Int = 0 + + override fun append(seq: CharSequence): Appendable = apply { + seq.split('\n').forEachIndexed { index, line -> + if (index > 0) { + builder.append('\n') + } + appendIndentIfNeeded() + builder.append(line) + } + } + + override fun append(seq: CharSequence, start: Int, end: Int): Appendable = apply { + append(seq.subSequence(start, end)) + } + + override fun append(c: Char): Appendable = apply { + if (c != '\n') { + appendIndentIfNeeded() + } + builder.append(c) + } + + public inline fun withIndent(block: PrettyPrinter.() -> Unit) { + indent += 1 + block(this) + indent -= 1 + } + + public inline fun withIndentInBraces(block: PrettyPrinter.() -> Unit) { + withIndentWrapped(before = "{", after = "}", block) + } + + public inline fun withIndentInSquareBrackets(block: PrettyPrinter.() -> Unit) { + withIndentWrapped(before = "[", after = "]", block) + } + + public inline fun withIndentWrapped(before: String, after: String, block: PrettyPrinter.() -> Unit) { + append(before) + appendLine() + withIndent(block) + appendLine() + append(after) + } + + public inline fun printCollection( + collection: Iterable, + separator: String = ", ", + prefix: String = "", + postfix: String = "", + renderItem: PrettyPrinter.(T) -> Unit + ) { + append(prefix) + val iterator = collection.iterator() + while (iterator.hasNext()) { + renderItem(iterator.next()) + if (iterator.hasNext()) { + append(separator) + } + } + append(postfix) + } + + + public inline fun printCollectionIfNotEmpty( + collection: Iterable, + separator: String = ", ", + prefix: String = "", + postfix: String = "", + renderItem: PrettyPrinter.(T) -> Unit + ) { + if (!collection.iterator().hasNext()) return + printCollection(collection, separator, prefix, postfix, renderItem) + } + + public fun printCharIfNotThere(char: Char) { + if (builder.lastOrNull() != char) { + append(char) + } + } + + private fun appendIndentIfNeeded() { + if (builder.isEmpty() || builder[builder.lastIndex] == '\n') { + builder.append(" ".repeat(indentSize * indent)) + } + } + + override fun toString(): String { + return builder.toString() + } +} + +public inline fun prettyPrint(body: PrettyPrinter.() -> Unit): String = + PrettyPrinter().apply(body).toString() \ No newline at end of file