mirror of https://github.com/sbt/sbt.git
Merge pull request #218 from eatkins/rework-supershell-interlacing
Fix supershell position bug
This commit is contained in:
commit
2086d3be5a
|
|
@ -310,74 +310,6 @@ object ConsoleAppender {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Caches the task progress lines so that they can be reprinted whenever the default
|
|
||||||
* appender logs additional messages. This makes it so that the progress lines are
|
|
||||||
* more stable with less appearance of flickering.
|
|
||||||
*/
|
|
||||||
private object SuperShellLogger {
|
|
||||||
private val progressLines = new AtomicReference[Seq[String]](Nil)
|
|
||||||
// leave some blank lines for tasks that might use println(...)
|
|
||||||
private val blankZone = 5
|
|
||||||
private val padding = new AtomicInteger(0)
|
|
||||||
private val ascii = "[^\\p{ASCII}]".r.pattern
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Splits a log message into individual lines and interlaces each line with
|
|
||||||
* the task progress report to reduce the appearance of flickering. It is assumed
|
|
||||||
* that this method is only called while holding the out.lockObject.
|
|
||||||
*/
|
|
||||||
private[util] def writeMsg(out: ConsoleOut, msg: String): Unit = {
|
|
||||||
val progress = progressLines.get
|
|
||||||
msg.linesIterator.foreach { l =>
|
|
||||||
out.println(s"$DeleteLine$l")
|
|
||||||
if (progress.length > 0) {
|
|
||||||
val stripped = ascii.matcher(l).replaceAll("")
|
|
||||||
val isDebugLine =
|
|
||||||
l.startsWith("[debug]") || l.startsWith(s"${scala.Console.RESET}[debug]")
|
|
||||||
// As long as the line isn't a debug line, we can assume it was printed to
|
|
||||||
// the console and reduce the top padding.
|
|
||||||
val pad = if (padding.get > 0 && !isDebugLine) padding.decrementAndGet else padding.get
|
|
||||||
deleteConsoleLines(out, blankZone + pad)
|
|
||||||
progress.foreach(out.println)
|
|
||||||
out.print(cursorUp(blankZone + progress.length + padding.get))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives a new task report and replaces the old one. In the event that the new
|
|
||||||
* report has fewer lines than the previous report, padding lines are added on top
|
|
||||||
* so that the console log lines remain contiguous. When a console line is printed
|
|
||||||
* at the info or greater level, we can decrement the padding because the console
|
|
||||||
* line will have filled in the blank line.
|
|
||||||
*/
|
|
||||||
private[util] def update(out: ConsoleOut, pe: ProgressEvent): Unit = {
|
|
||||||
val sorted = pe.items.sortBy(x => x.elapsedMicros)
|
|
||||||
val info = sorted map { item =>
|
|
||||||
val elapsed = item.elapsedMicros / 1000000L
|
|
||||||
s"$DeleteLine | => ${item.name} ${elapsed}s"
|
|
||||||
}
|
|
||||||
|
|
||||||
val previousLines = progressLines.getAndSet(info)
|
|
||||||
val prevPadding = padding.get
|
|
||||||
val newPadding = math.max(0, previousLines.length + prevPadding - info.length)
|
|
||||||
padding.set(newPadding)
|
|
||||||
|
|
||||||
deleteConsoleLines(out, newPadding)
|
|
||||||
deleteConsoleLines(out, blankZone)
|
|
||||||
info.foreach(i => out.println(i))
|
|
||||||
|
|
||||||
out.print(cursorUp(blankZone + info.length + newPadding))
|
|
||||||
out.flush()
|
|
||||||
}
|
|
||||||
private def deleteConsoleLines(out: ConsoleOut, n: Int): Unit = {
|
|
||||||
(1 to n) foreach { _ =>
|
|
||||||
out.println(DeleteLine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// See http://stackoverflow.com/questions/24205093/how-to-create-a-custom-appender-in-log4j2
|
// See http://stackoverflow.com/questions/24205093/how-to-create-a-custom-appender-in-log4j2
|
||||||
// for custom appender using Java.
|
// for custom appender using Java.
|
||||||
// http://logging.apache.org/log4j/2.x/manual/customconfig.html
|
// http://logging.apache.org/log4j/2.x/manual/customconfig.html
|
||||||
|
|
@ -398,6 +330,64 @@ class ConsoleAppender private[ConsoleAppender] (
|
||||||
) extends AbstractAppender(name, null, LogExchange.dummyLayout, true, Array.empty) {
|
) extends AbstractAppender(name, null, LogExchange.dummyLayout, true, Array.empty) {
|
||||||
import scala.Console.{ BLUE, GREEN, RED, YELLOW }
|
import scala.Console.{ BLUE, GREEN, RED, YELLOW }
|
||||||
|
|
||||||
|
private val progressState: AtomicReference[ProgressState] = new AtomicReference(null)
|
||||||
|
private[sbt] def setProgressState(state: ProgressState) = progressState.set(state)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a log message into individual lines and interlaces each line with
|
||||||
|
* the task progress report to reduce the appearance of flickering. It is assumed
|
||||||
|
* that this method is only called while holding the out.lockObject.
|
||||||
|
*/
|
||||||
|
private def supershellInterlaceMsg(msg: String): Unit = {
|
||||||
|
val state = progressState.get
|
||||||
|
import state._
|
||||||
|
val progress = progressLines.get
|
||||||
|
msg.linesIterator.foreach { l =>
|
||||||
|
out.println(s"$DeleteLine$l")
|
||||||
|
if (progress.length > 0) {
|
||||||
|
val pad = if (padding.get > 0) padding.decrementAndGet() else 0
|
||||||
|
deleteConsoleLines(blankZone + pad)
|
||||||
|
progress.foreach(out.println)
|
||||||
|
out.print(cursorUp(blankZone + progress.length + padding.get))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives a new task report and replaces the old one. In the event that the new
|
||||||
|
* report has fewer lines than the previous report, padding lines are added on top
|
||||||
|
* so that the console log lines remain contiguous. When a console line is printed
|
||||||
|
* at the info or greater level, we can decrement the padding because the console
|
||||||
|
* line will have filled in the blank line.
|
||||||
|
*/
|
||||||
|
private def updateProgressState(pe: ProgressEvent): Unit = {
|
||||||
|
val state = progressState.get
|
||||||
|
import state._
|
||||||
|
val sorted = pe.items.sortBy(x => x.elapsedMicros)
|
||||||
|
val info = sorted map { item =>
|
||||||
|
val elapsed = item.elapsedMicros / 1000000L
|
||||||
|
s"$DeleteLine | => ${item.name} ${elapsed}s"
|
||||||
|
}
|
||||||
|
|
||||||
|
val previousLines = progressLines.getAndSet(info)
|
||||||
|
val prevPadding = padding.get
|
||||||
|
val newPadding = math.max(0, previousLines.length + prevPadding - info.length)
|
||||||
|
padding.set(newPadding)
|
||||||
|
|
||||||
|
deleteConsoleLines(newPadding)
|
||||||
|
deleteConsoleLines(blankZone)
|
||||||
|
info.foreach(i => out.println(i))
|
||||||
|
|
||||||
|
out.print(cursorUp(blankZone + info.length + newPadding))
|
||||||
|
out.flush()
|
||||||
|
}
|
||||||
|
private def deleteConsoleLines(n: Int): Unit = {
|
||||||
|
(1 to n) foreach { _ =>
|
||||||
|
out.println(DeleteLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val reset: String = {
|
private val reset: String = {
|
||||||
if (ansiCodesSupported && useFormat) scala.Console.RESET
|
if (ansiCodesSupported && useFormat) scala.Console.RESET
|
||||||
else ""
|
else ""
|
||||||
|
|
@ -525,8 +515,8 @@ class ConsoleAppender private[ConsoleAppender] (
|
||||||
private def write(msg: String): Unit = {
|
private def write(msg: String): Unit = {
|
||||||
val toWrite =
|
val toWrite =
|
||||||
if (!useFormat || !ansiCodesSupported) EscHelpers.removeEscapeSequences(msg) else msg
|
if (!useFormat || !ansiCodesSupported) EscHelpers.removeEscapeSequences(msg) else msg
|
||||||
if (ConsoleAppender.showProgress) {
|
if (progressState.get != null) {
|
||||||
SuperShellLogger.writeMsg(out, toWrite)
|
supershellInterlaceMsg(toWrite)
|
||||||
} else {
|
} else {
|
||||||
out.println(toWrite)
|
out.println(toWrite)
|
||||||
}
|
}
|
||||||
|
|
@ -560,10 +550,8 @@ class ConsoleAppender private[ConsoleAppender] (
|
||||||
}
|
}
|
||||||
|
|
||||||
private def appendProgressEvent(pe: ProgressEvent): Unit =
|
private def appendProgressEvent(pe: ProgressEvent): Unit =
|
||||||
if (ConsoleAppender.showProgress) {
|
if (progressState.get != null) {
|
||||||
out.lockObject.synchronized {
|
out.lockObject.synchronized(updateProgressState(pe))
|
||||||
SuperShellLogger.update(out, pe)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def appendMessageContent(level: Level.Value, o: AnyRef): Unit = {
|
private def appendMessageContent(level: Level.Value, o: AnyRef): Unit = {
|
||||||
|
|
@ -596,3 +584,14 @@ class ConsoleAppender private[ConsoleAppender] (
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SuppressedTraceContext(val traceLevel: Int, val useFormat: Boolean)
|
final class SuppressedTraceContext(val traceLevel: Int, val useFormat: Boolean)
|
||||||
|
private[sbt] final class ProgressState(
|
||||||
|
val progressLines: AtomicReference[Seq[String]],
|
||||||
|
val padding: AtomicInteger,
|
||||||
|
val blankZone: Int
|
||||||
|
) {
|
||||||
|
def this(blankZone: Int) = this(new AtomicReference(Nil), new AtomicInteger(0), blankZone)
|
||||||
|
def reset(): Unit = {
|
||||||
|
progressLines.set(Nil)
|
||||||
|
padding.set(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue