io.exoquery.pprint.PPrinter.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pprint-kotlin Show documentation
Show all versions of pprint-kotlin Show documentation
Pretty Printing for Kotlin
package io.exoquery.pprint
import io.exoquery.fansi.Attrs
import java.util.SortedMap
import kotlin.reflect.full.*
object EncodeHelperImpl: EncodeHelper() {
override fun makeHexString(c: Char): String = "\\u%04x".format(c.code)
}
open class PPrinter(override val config: PPrinterConfig = PPrinterConfig()): PPrinterBase(config) {
open override fun treeify(x: Any?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree {
fun treeifySame(x: Any?) = treeify(x, escapeUnicode, showFieldNames)
fun applyArray(name: String, seq: Sequence) =
Tree.Apply(name, seq.map {x -> treeifySame(x)}.iterator())
return when {
x == null -> Tree.Literal("null")
x is Boolean -> Tree.Literal(x.toString())
x is Char -> EncodeHelperImpl.encodeChar(x, escapeUnicode)
x is Byte -> Tree.Literal(x.toString())
x is Short -> Tree.Literal(x.toString())
x is Int -> Tree.Literal(x.toString())
x is Long -> Tree.Literal(x.toString() + "L")
x is Float -> Tree.Literal(x.toString() + "F")
x is Double -> Tree.Literal(x.toString())
x is String -> EncodeHelperImpl.encodeString(x, escapeUnicode)
// No Symbol in Kotlin
//x is Symbol -> Tree.Literal("'" + x.name)
x is Map<*, *> -> {
val name =
if (showGenericForCollections) {
val superTypes = x::class.allSuperclasses.toSet()
when {
(SortedMap::class in superTypes) -> "SortedMap"
(LinkedHashMap::class in superTypes) -> "LinkedHashMap"
else -> "Map"
}
} else {
x::class.simpleName ?: "Map"
}
Tree.Apply(
name,
x.asSequence().flatMap { (k, v) ->
listOf(Tree.Infix(treeify(k, escapeUnicode, showFieldNames), "->", treeify(v, escapeUnicode, showFieldNames)))
}.iterator()
)
}
// Note: Maybe want to have a configuration to always make it just "Sequence"
x is Sequence<*> -> {
val name = if (showGenericForCollections) "Sequence" else x::class.simpleName ?: "Map"
Tree.Apply(name, x.asSequence().map { x -> treeify(x, escapeUnicode, showFieldNames) }.iterator())
}
// Note: Maybe want to have a configuration to always make it just "Iterable"
x is Iterable<*> -> {
val name =
if (showGenericForCollections) {
val superTypes = x::class.allSuperclasses.toSet()
when {
(List::class in superTypes) -> "List"
(Set::class in superTypes) -> "Set"
else -> x::class.simpleName ?: "Iterable"
}
} else {
x::class.simpleName ?: "Iterable"
}
Tree.Apply(name, x.asSequence().map { x -> treeify(x, escapeUnicode, showFieldNames) }.iterator())
}
// No None in kotlin
//case None -> Tree.Literal("None")
x is Iterator<*> -> {
if (!x.hasNext())
Tree.Literal("empty iterator")
else
Tree.Literal("non-empty iterator")
}
x is BooleanArray -> applyArray("BooleanArray", x.asSequence())
x is ByteArray -> applyArray("ByteArray", x.asSequence())
x is CharArray -> applyArray("CharArray", x.asSequence())
x is DoubleArray -> applyArray("DoubleArray", x.asSequence())
x is FloatArray -> applyArray("FloatArray", x.asSequence())
x is IntArray -> applyArray("IntArray", x.asSequence())
x is LongArray -> applyArray("LongArray", x.asSequence())
x is ShortArray -> applyArray("ShortArray", x.asSequence())
x is Array<*> -> applyArray("Array", x.asSequence())
x is Sequence<*> -> Tree.Apply("Sequence", x.map {x -> treeifySame(x)}.iterator())
x is Pair<*, *> -> {
// We could also do Tree.Infix(treeifySame(x.first), "to", treeifySame(x.second)), so it would be "a to b" not sure if that is better or worse
Tree.Apply("Pair", sequenceOf(treeifySame(x.first), treeifySame(x.second)).iterator())
}
x is Triple<*, *, *> ->
Tree.Apply("Triple", sequenceOf(treeifySame(x.first), treeifySame(x.second), treeifySame(x.third)).iterator())
(x::class.isData) -> {
val cls = x::class
val className = cls.simpleName ?: cls.toString()
val productArity = cls.constructors.firstOrNull()?.parameters?.size ?: 0
// If there are no constructors it's usually a `data object`
if (productArity == 0) Tree.Lazy { ctx -> sequenceOf(x.toString()).iterator() }
// Don't do "a to b" when product has 2 elements since there's a specific Pair/Triple in Kotlin
//else if(productArity == 2 && Util.isOperator(x.productPrefix)){
// Tree.Infix(
// treeify(x.productElement(0), escapeUnicode, showFieldNames),
// x.productPrefix,
// treeify(x.productElement(1), escapeUnicode, showFieldNames)
// )
// }
//else (className.startsWith(tuplePrefix), className.lift(tuplePrefix.length)) match{
// // leave out tuple1, so it gets printed as Tuple1(foo) instead of (foo)
// // Don't check the whole suffix, because of specialization there may be
// // funny characters after the digit
// case (true, Some('2' | '3' | '4' | '5' | '6' | '7' | '8' | '9')) ->
// Tree.Apply("", x.productIterator.map(x -> treeify(x, escapeUnicode, showFieldNames)))
// case _ ->
// Tree.Apply(x.productPrefix, ProductSupport.treeifyProductElements(x, this, escapeUnicode, showFieldNames))
//}
else {
Tree.Apply(className, ProductSupport.treeifyProductElements(x, cls, this, escapeUnicode, showFieldNames))
}
}
else -> Tree.Lazy { ctx ->
val v = x.toString()
//when {
// x.toString() == null -> "null" // It appears that in Kotlin x.toString() can never be null and we checked x for nullity at the top of this function
// else -> x.toString()
//}
sequenceOf(v).iterator()
}
}
}
companion object {
val Color = PPrinter(PPrinterConfig())
val BlackWhite = PPrinter(
PPrinterConfig().copy(
colorLiteral = Attrs.Empty,
colorApplyPrefix = Attrs.Empty
)
)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy