Some fixes to emoji/ANSI renderer

This commit is contained in:
Mike Hearn 2017-03-15 21:14:50 +01:00
parent f079c05e6a
commit eb21458885
3 changed files with 63 additions and 44 deletions

View File

@ -7,7 +7,9 @@ import net.corda.core.codePointsString
*/
object Emoji {
// Unfortunately only Apple has a terminal that can do colour emoji AND an emoji font installed by default.
val hasEmojiTerminal by lazy { listOf("Apple_Terminal", "iTerm.app").contains(System.getenv("TERM_PROGRAM")) }
val hasEmojiTerminal by lazy {
System.getenv("CORDA_FORCE_EMOJI") != null || System.getenv("TERM_PROGRAM") in listOf("Apple_Terminal", "iTerm.app")
}
@JvmStatic val CODE_SANTA_CLAUS: String = codePointsString(0x1F385)
@JvmStatic val CODE_DIAMOND: String = codePointsString(0x1F537)
@ -32,14 +34,21 @@ object Emoji {
val diamond: String get() = if (emojiMode.get() != null) "$CODE_DIAMOND " else ""
val bagOfCash: String get() = if (emojiMode.get() != null) "$CODE_BAG_OF_CASH " else ""
val newspaper: String get() = if (emojiMode.get() != null) "$CODE_NEWSPAPER " else ""
val rightArrow: String get() = if (emojiMode.get() != null) "$CODE_RIGHT_ARROW " else ""
val leftArrow: String get() = if (emojiMode.get() != null) "$CODE_LEFT_ARROW " else ""
val paperclip: String get() = if (emojiMode.get() != null) "$CODE_PAPERCLIP " else ""
val coolGuy: String get() = if (emojiMode.get() != null) "$CODE_COOL_GUY " else ""
val books: String get() = if (emojiMode.get() != null) "$CODE_BOOKS " else ""
// These have old/non-emoji symbols with better platform support.
val greenTick: String get() = if (emojiMode.get() != null) "$CODE_GREEN_TICK " else ""
val rightArrow: String get() = if (emojiMode.get() != null) "$CODE_RIGHT_ARROW " else "▶︎"
val skullAndCrossbones: String get() = if (emojiMode.get() != null) "$CODE_SKULL_AND_CROSSBONES " else ""
val noEntry: String get() = if (emojiMode.get() != null) "$CODE_NO_ENTRY " else ""
inline fun <T> renderIfSupported(body: () -> T): T {
emojiMode.set(this) // Could be any object.
if (hasEmojiTerminal)
emojiMode.set(this) // Could be any object.
try {
return body()
} finally {

View File

@ -57,6 +57,7 @@ class ProgressTracker(vararg steps: Step) {
open fun childProgressTracker(): ProgressTracker? = null
}
// TODO: There's no actual way to create these steps anymore!
/** This class makes it easier to relabel a step on the fly, to provide transient information. */
open inner class RelabelableStep(currentLabel: String) : Step(currentLabel) {
override val changes: BehaviorSubject<Change> = BehaviorSubject.create()

View File

@ -1,9 +1,6 @@
package net.corda.node.utilities
import net.corda.core.utilities.Emoji.CODE_GREEN_TICK
import net.corda.core.utilities.Emoji.CODE_NO_ENTRY
import net.corda.core.utilities.Emoji.CODE_RIGHT_ARROW
import net.corda.core.utilities.Emoji.CODE_SKULL_AND_CROSSBONES
import net.corda.core.utilities.Emoji
import net.corda.core.utilities.ProgressTracker
import net.corda.node.utilities.ANSIProgressRenderer.progressTracker
import org.apache.logging.log4j.LogManager
@ -43,12 +40,22 @@ object ANSIProgressRenderer {
}
// Reset the state when a new tracker is wired up.
prevMessagePrinted = null
prevLinesDrawn = 0
draw(true)
subscription = value?.changes?.subscribe({ draw(true) }, { draw(true, it) }, { progressTracker = null; draw(true) })
if (value != null) {
prevMessagePrinted = null
prevLinesDrawn = 0
draw(true)
subscription = value.changes.subscribe({ draw(true) }, { done(it) }, { done(null) })
}
}
var onDone: () -> Unit = {}
private fun done(error: Throwable?) {
if (error == null) progressTracker = null
draw(true, error)
onDone()
}
private fun setup() {
AnsiConsole.systemInstall()
@ -62,7 +69,7 @@ object ANSIProgressRenderer {
// than doing things the official way with a dedicated plugin, etc, as it avoids mucking around with all
// the config XML and lifecycle goop.
val manager = LogManager.getContext(false) as LoggerContext
val consoleAppender = manager.configuration.appenders.values.filterIsInstance<ConsoleAppender>().single()
val consoleAppender = manager.configuration.appenders.values.filterIsInstance<ConsoleAppender>().single { it.name == "Console-Appender" }
val scrollingAppender = object : AbstractOutputStreamAppender<OutputStreamManager>(
consoleAppender.name, consoleAppender.layout, consoleAppender.filter,
consoleAppender.ignoreExceptions(), true, consoleAppender.manager) {
@ -115,39 +122,41 @@ object ANSIProgressRenderer {
return
}
// Handle the case where the number of steps in a progress tracker is changed during execution.
val ansi = Ansi.ansi()
if (prevLinesDrawn > 0 && moveUp)
ansi.cursorUp(prevLinesDrawn)
Emoji.renderIfSupported {
// Handle the case where the number of steps in a progress tracker is changed during execution.
val ansi = Ansi.ansi()
if (prevLinesDrawn > 0 && moveUp)
ansi.cursorUp(prevLinesDrawn)
// Put a blank line between any logging and us.
ansi.eraseLine()
ansi.newline()
val pt = progressTracker ?: return
var newLinesDrawn = 1 + pt.renderLevel(ansi, 0, error != null)
if (error != null) {
// TODO: This should be using emoji only on supported platforms.
ansi.a("$CODE_SKULL_AND_CROSSBONES $error")
ansi.eraseLine(Ansi.Erase.FORWARD)
// Put a blank line between any logging and us.
ansi.eraseLine()
ansi.newline()
newLinesDrawn++
}
val pt = progressTracker ?: return
var newLinesDrawn = 1 + pt.renderLevel(ansi, 0, error != null)
if (newLinesDrawn < prevLinesDrawn) {
// If some steps were removed from the progress tracker, we don't want to leave junk hanging around below.
val linesToClear = prevLinesDrawn - newLinesDrawn
repeat(linesToClear) {
ansi.eraseLine()
if (error != null) {
ansi.a("${Emoji.skullAndCrossbones} ${error.message}")
ansi.eraseLine(Ansi.Erase.FORWARD)
ansi.newline()
newLinesDrawn++
}
ansi.cursorUp(linesToClear)
}
prevLinesDrawn = newLinesDrawn
// Need to force a flush here in order to ensure stderr/stdout sync up properly.
System.out.print(ansi)
System.out.flush()
if (newLinesDrawn < prevLinesDrawn) {
// If some steps were removed from the progress tracker, we don't want to leave junk hanging around below.
val linesToClear = prevLinesDrawn - newLinesDrawn
repeat(linesToClear) {
ansi.eraseLine()
ansi.newline()
}
ansi.cursorUp(linesToClear)
}
prevLinesDrawn = newLinesDrawn
// Need to force a flush here in order to ensure stderr/stdout sync up properly.
System.out.print(ansi)
System.out.flush()
}
}
// Returns number of lines rendered.
@ -160,11 +169,11 @@ object ANSIProgressRenderer {
if (indent > 0 && step == ProgressTracker.DONE) continue
val marker = when {
index < stepIndex -> "$CODE_GREEN_TICK "
index == stepIndex && step == ProgressTracker.DONE -> "$CODE_GREEN_TICK "
index == stepIndex -> "$CODE_RIGHT_ARROW "
error -> "$CODE_NO_ENTRY "
else -> " "
index < stepIndex -> "${Emoji.greenTick} "
index == stepIndex && step == ProgressTracker.DONE -> "${Emoji.greenTick} "
index == stepIndex -> "${Emoji.rightArrow} "
error -> "${Emoji.noEntry} "
else -> " " // Not reached yet.
}
a(" ".repeat(indent))
a(marker)