Skip to content
Permalink
Browse files

Use Unicode symbols in tree and table dumps for better readability.

This is controlled by the new config option slick.unicodeDump and
enabled by default in testkit/test.
  • Loading branch information
szeiger committed Aug 11, 2014
1 parent f54d272 commit 82b258f2d00c53b0eefc09d07704868ce1a4e4fe
@@ -1 +1,4 @@
slick.ansiDump = true
slick {
ansiDump = true
unicodeDump = true
}
@@ -3,6 +3,9 @@ slick {
# Use ANSI color sequences in tree dumps
ansiDump = false

# Use Unicode box characters in tree and table dumps
unicodeDump = false

# Dump individual Select and Ref nodes instead of a single Path
dumpPaths = false
}
@@ -16,6 +16,9 @@ object GlobalConfig {
/** Use ANSI color sequences in tree dumps */
val ansiDump = config.getBoolean("slick.ansiDump")

/** Use Unixode box characters in table dumps */
val unicodeDump = config.getBoolean("slick.unicodeDump")

/** Get a `Config` object for a Slick driver */
def driverConfig(name: String): Config = {
val path = "slick.driver." + name
@@ -4,7 +4,8 @@ import org.slf4j.{ Logger => Slf4jLogger, LoggerFactory }

final class SlickLogger(val slf4jLogger: Slf4jLogger) {
@inline
def debug(msg: => String, n: => Dumpable): Unit = debug(msg+"\n"+TreeDump.get(n, prefix = DumpInfo.highlight("| ")))
def debug(msg: => String, n: => Dumpable): Unit =
debug(msg+"\n"+TreeDump.get(n, prefix = DumpInfo.highlight(if(GlobalConfig.unicodeDump) "\u2503 " else "| ")))

@inline
def isDebugEnabled = slf4jLogger.isDebugEnabled()
@@ -4,7 +4,10 @@ import scala.collection.mutable.ArrayBuffer

/** Utility methods for creating result set debug output. */
class TableDump(maxColumnWidth: Int = 20) {
protected[this] val dashes = Iterator.fill(maxColumnWidth+2)('-').mkString
protected[this] val box: IndexedSeq[String] =
(if(GlobalConfig.unicodeDump) "\u2501\u250f\u2533\u2513\u2523\u254b\u252b\u2517\u253b\u251b\u2503" else "-/+\\|+|\\+/|").map(_.toString)

protected[this] val dashes = Iterator.fill(maxColumnWidth+2)(box(0)).mkString
protected[this] val spaces = Iterator.fill(maxColumnWidth+2)(' ').mkString

protected[this] def formatLine(line: IndexedSeq[Any]): IndexedSeq[String] = line.map { v =>
@@ -16,22 +19,45 @@ class TableDump(maxColumnWidth: Int = 20) {
val texts = formatLine(header) +: data.map(formatLine)
val widths = 0.until(columns).map { idx => math.min(maxColumnWidth, texts.map(_.apply(idx).length).max) }
val buf = new ArrayBuffer[String](data.length + 4)
buf += TreeDump.blue + widths.map(l => dashes.substring(0, l+2)).mkString("/", "+", "\\") + TreeDump.normal
buf += TreeDump.blue + widths.map(l => dashes.substring(0, l+2)).mkString(box(1), box(2), box(3)) + TreeDump.normal
var first = true
def pad(s: String, len: Int): String = {
if(s.length > maxColumnWidth) s.substring(0, maxColumnWidth-3) + TreeDump.cyan+"..."
else s + spaces.substring(0, len-s.length)
val slen = s.codePointCount(0, s.length)
if(slen > maxColumnWidth) {
if(slen == s.length) s.substring(0, maxColumnWidth-3) else limitCodepoints(s, maxColumnWidth-3)
} + TreeDump.cyan+"..."
else s + spaces.substring(0, len-slen)
}
for(line <- texts) {
if(first) {
buf += (line, widths).zipped.map((s, len) => TreeDump.yellow+" "+pad(s, len)+" ").mkString(TreeDump.blue+"|", TreeDump.blue+"|", TreeDump.blue+"|"+TreeDump.normal)
buf += TreeDump.blue + widths.map(l => dashes.substring(0, l+2)).mkString("+", "+", "+") + TreeDump.normal
buf += (line, widths).zipped.map((s, len) => TreeDump.yellow+" "+pad(s, len)+" ").mkString(TreeDump.blue+box(10), TreeDump.blue+box(10), TreeDump.blue+box(10)+TreeDump.normal)
buf += TreeDump.blue + widths.map(l => dashes.substring(0, l+2)).mkString(box(4), box(5), box(6)) + TreeDump.normal
first = false
} else {
buf += (line, widths).zipped.map((s, len) => TreeDump.normal+" "+pad(s, len)+" ").mkString(TreeDump.blue+"|", TreeDump.blue+"|", TreeDump.blue+"|"+TreeDump.normal)
buf += (line, widths).zipped.map((s, len) => TreeDump.normal+" "+pad(s, len)+" ").mkString(TreeDump.blue+box(10), TreeDump.blue+box(10), TreeDump.blue+box(10)+TreeDump.normal)
}
}
buf += TreeDump.blue + widths.map(l => dashes.substring(0, l+2)).mkString("\\", "+", "/") + TreeDump.normal
buf += TreeDump.blue + widths.map(l => dashes.substring(0, l+2)).mkString(box(7), box(8), box(9)) + TreeDump.normal
buf
}

/** Return the first `len` codepoints from a String */
protected[this] def limitCodepoints(s: String, len: Int): String = {
val b = new StringBuilder(s.length, "")
var i = 0
var cps = 0
while(cps < len) {
val c = s.charAt(i)
if(Character.isHighSurrogate(c)) {
b.append(c)
b.append(s.charAt(i+1))
i += 1
} else {
b.append(c)
}
cps += 1
i += 1
}
b.toString
}
}
@@ -4,10 +4,13 @@ import java.io.{OutputStreamWriter, StringWriter, PrintWriter}

/** Create a readable printout of a tree. */
object TreeDump {
private[util] val (normal, green, yellow, blue, cyan) = {
private[util] val (normal, green, yellow, blue, cyan) =
if(GlobalConfig.ansiDump) ("\u001B[0m", "\u001B[32m", "\u001B[33m", "\u001B[34m", "\u001B[36m")
else ("", "", "", "", "")
}

private[this] val (childPrefix1, childPrefix2, lastChildPrefix1, lastChildPrefix2) =
if(GlobalConfig.unicodeDump) ("\u2523 ", "\u2503 ", "\u2517 ", " ")
else (" ", " ", " ", " ")

def get(n: Dumpable, name: String = "", prefix: String = "") = {
val buf = new StringWriter
@@ -16,18 +19,23 @@ object TreeDump {
}

def apply(n: Dumpable, name: String = "", prefix: String = "", out: PrintWriter = new PrintWriter(new OutputStreamWriter(System.out))) {
def dump(value: Dumpable, prefix: String, name: String, topLevel: Boolean) {
def dump(value: Dumpable, prefix1: String, prefix2: String, name: String, level: Int) {
val di = value.getDumpInfo
out.println(
prefix +
prefix1 +
cyan + (if(name.nonEmpty) name + ": " else "") +
yellow + di.name + (if(di.name.nonEmpty && di.mainInfo.nonEmpty) " " else "") +
normal + di.mainInfo +
(if(di.attrInfo.isEmpty) "" else " " + blue + di.attrInfo + normal)
)
di.children.foreach { case (name, value) => dump(value, prefix + " ", name, false) }
val children = di.children.toIndexedSeq
children.zipWithIndex.foreach { case ((name, value), idx) =>
val (p1, p2) = if(idx == children.size-1) (lastChildPrefix1, lastChildPrefix2) else (childPrefix1, childPrefix2)
val (cp1, cp2) = if(level % 2 == 0) (blue + p1, blue + p2) else (green + p1, green + p2)
dump(value, prefix2 + cp1, prefix2 + cp2, name, level + 1)
}
}
dump(n, prefix, name, true)
dump(n, prefix, prefix, name, 0)
out.flush()
}
}

0 comments on commit 82b258f

Please sign in to comment.
You can’t perform that action at this time.