Move ProgressState into its own file

This didn't really belong in ConsoleAppender.scala anymore.
This commit is contained in:
Ethan Atkins 2020-07-07 14:06:55 -07:00
parent b6354656b1
commit ed4d40d3e2
2 changed files with 158 additions and 138 deletions

View File

@ -9,7 +9,7 @@ package sbt.internal.util
import java.io.{ PrintStream, PrintWriter }
import java.lang.StringBuilder
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger, AtomicReference }
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
import org.apache.logging.log4j.core.appender.AbstractAppender
import org.apache.logging.log4j.core.{ LogEvent => XLogEvent }
@ -18,8 +18,6 @@ import org.apache.logging.log4j.{ Level => XLevel }
import sbt.internal.util.ConsoleAppender._
import sbt.util._
import scala.collection.mutable.ArrayBuffer
object ConsoleLogger {
// These are provided so other modules do not break immediately.
@deprecated("Use EscHelpers.ESC instead", "0.13.x")
@ -553,138 +551,3 @@ class ConsoleAppender private[ConsoleAppender] (
}
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,
val currentLineBytes: AtomicReference[ArrayBuffer[Byte]],
) {
def this(blankZone: Int) =
this(
new AtomicReference(Nil),
new AtomicInteger(0),
blankZone,
new AtomicReference(new ArrayBuffer[Byte]),
)
def reset(): Unit = {
progressLines.set(Nil)
padding.set(0)
currentLineBytes.set(new ArrayBuffer[Byte])
}
private[util] def clearBytes(): Unit = {
val pad = padding.get
if (currentLineBytes.get.isEmpty && pad > 0) padding.decrementAndGet()
currentLineBytes.set(new ArrayBuffer[Byte])
}
private[util] def addBytes(terminal: Terminal, bytes: ArrayBuffer[Byte]): Unit = {
val previous = currentLineBytes.get
val padding = this.padding.get
val prevLineCount = if (padding > 0) terminal.lineCount(new String(previous.toArray)) else 0
previous ++= bytes
if (padding > 0) {
val newLineCount = terminal.lineCount(new String(previous.toArray))
val diff = newLineCount - prevLineCount
this.padding.set(math.max(padding - diff, 0))
}
}
private[util] def printPrompt(terminal: Terminal, printStream: PrintStream): Unit =
if (terminal.prompt != Prompt.Running && terminal.prompt != Prompt.Batch) {
val prefix = if (terminal.isAnsiSupported) s"$DeleteLine$CursorLeft1000" else ""
val pmpt = prefix.getBytes ++ terminal.prompt.render().getBytes
pmpt.foreach(b => printStream.write(b & 0xFF))
}
private[util] def reprint(terminal: Terminal, printStream: PrintStream): Unit = {
printPrompt(terminal, printStream)
if (progressLines.get.nonEmpty) {
val lines = printProgress(terminal, terminal.getLastLine.getOrElse(""))
printStream.print(ClearScreenAfterCursor + lines)
}
}
private[util] def printProgress(
terminal: Terminal,
lastLine: String
): String = {
val previousLines = progressLines.get
if (previousLines.nonEmpty) {
val currentLength = previousLines.foldLeft(0)(_ + terminal.lineCount(_))
val (height, width) = terminal.getLineHeightAndWidth(lastLine)
val left = cursorLeft(1000) // resets the position to the left
val offset = width > 0
val pad = math.max(padding.get - height, 0)
val start = (if (offset) "\n" else "")
val totalSize = currentLength + blankZone + pad
val blank = left + s"\n$DeleteLine" * (totalSize - currentLength)
val lines = previousLines.mkString(DeleteLine, s"\n$DeleteLine", s"\n$DeleteLine")
val resetCursorUp = cursorUp(totalSize + (if (offset) 1 else 0))
val resetCursor = resetCursorUp + left + lastLine
start + blank + lines + resetCursor
} else {
ClearScreenAfterCursor
}
}
}
private[sbt] object ProgressState {
/**
* 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[sbt] def updateProgressState(
pe: ProgressEvent,
terminal: Terminal
): Unit = {
val state = terminal.progressState
val isRunning = terminal.prompt == Prompt.Running
val isBatch = terminal.prompt == Prompt.Batch
val isWatch = terminal.prompt == Prompt.Watch
val noPrompt = terminal.prompt == Prompt.NoPrompt
if (terminal.isSupershellEnabled) {
if (!pe.skipIfActive.getOrElse(false) || (!isRunning && !isBatch)) {
terminal.withPrintStream { ps =>
val info =
if ((isRunning || isBatch || noPrompt) && pe.channelName
.fold(true)(_ == terminal.name)) {
pe.items.map { item =>
val elapsed = item.elapsedMicros / 1000000L
s" | => ${item.name} ${elapsed}s"
}
} else {
pe.command.toSeq.flatMap { cmd =>
val tail = if (isWatch) Nil else "enter 'cancel' to stop evaluation" :: Nil
s"sbt server is running '$cmd'" :: tail
}
}
val currentLength = info.foldLeft(0)(_ + terminal.lineCount(_))
val previousLines = state.progressLines.getAndSet(info)
val prevLength = previousLines.foldLeft(0)(_ + terminal.lineCount(_))
val lastLine = terminal.prompt match {
case Prompt.Running | Prompt.Batch => terminal.getLastLine.getOrElse("")
case a => a.render()
}
val prevSize = prevLength + state.padding.get
val newPadding = math.max(0, prevSize - currentLength)
state.padding.set(newPadding)
state.printPrompt(terminal, ps)
ps.print(state.printProgress(terminal, lastLine))
ps.flush()
}
} else if (state.progressLines.get.nonEmpty) {
state.progressLines.set(Nil)
terminal.withPrintStream { ps =>
val lastLine = terminal.getLastLine.getOrElse("")
ps.print(lastLine + ClearScreenAfterCursor)
ps.flush()
}
}
}
}
}

View File

@ -0,0 +1,157 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
import java.io.PrintStream
import java.util.concurrent.atomic.{ AtomicInteger, AtomicReference }
import sbt.internal.util.ConsoleAppender.{
ClearScreenAfterCursor,
CursorLeft1000,
DeleteLine,
cursorLeft,
cursorUp,
}
import scala.collection.mutable.ArrayBuffer
private[sbt] final class ProgressState(
val progressLines: AtomicReference[Seq[String]],
val padding: AtomicInteger,
val blankZone: Int,
val currentLineBytes: AtomicReference[ArrayBuffer[Byte]],
) {
def this(blankZone: Int) =
this(
new AtomicReference(Nil),
new AtomicInteger(0),
blankZone,
new AtomicReference(new ArrayBuffer[Byte]),
)
def reset(): Unit = {
progressLines.set(Nil)
padding.set(0)
currentLineBytes.set(new ArrayBuffer[Byte])
}
private[util] def clearBytes(): Unit = {
val pad = padding.get
if (currentLineBytes.get.isEmpty && pad > 0) padding.decrementAndGet()
currentLineBytes.set(new ArrayBuffer[Byte])
}
private[util] def addBytes(terminal: Terminal, bytes: ArrayBuffer[Byte]): Unit = {
val previous = currentLineBytes.get
val padding = this.padding.get
val prevLineCount = if (padding > 0) terminal.lineCount(new String(previous.toArray)) else 0
previous ++= bytes
if (padding > 0) {
val newLineCount = terminal.lineCount(new String(previous.toArray))
val diff = newLineCount - prevLineCount
this.padding.set(math.max(padding - diff, 0))
}
}
private[util] def printPrompt(terminal: Terminal, printStream: PrintStream): Unit =
if (terminal.prompt != Prompt.Running && terminal.prompt != Prompt.Batch) {
val prefix = if (terminal.isAnsiSupported) s"$DeleteLine$CursorLeft1000" else ""
val pmpt = prefix.getBytes ++ terminal.prompt.render().getBytes
pmpt.foreach(b => printStream.write(b & 0xFF))
}
private[util] def reprint(terminal: Terminal, printStream: PrintStream): Unit = {
printPrompt(terminal, printStream)
if (progressLines.get.nonEmpty) {
val lines = printProgress(terminal, terminal.getLastLine.getOrElse(""))
printStream.print(ClearScreenAfterCursor + lines)
}
}
private[util] def printProgress(
terminal: Terminal,
lastLine: String
): String = {
val previousLines = progressLines.get
if (previousLines.nonEmpty) {
val currentLength = previousLines.foldLeft(0)(_ + terminal.lineCount(_))
val (height, width) = terminal.getLineHeightAndWidth(lastLine)
val left = cursorLeft(1000) // resets the position to the left
val offset = width > 0
val pad = math.max(padding.get - height, 0)
val start = (if (offset) "\n" else "")
val totalSize = currentLength + blankZone + pad
val blank = left + s"\n$DeleteLine" * (totalSize - currentLength)
val lines = previousLines.mkString(DeleteLine, s"\n$DeleteLine", s"\n$DeleteLine")
val resetCursorUp = cursorUp(totalSize + (if (offset) 1 else 0))
val resetCursor = resetCursorUp + left + lastLine
start + blank + lines + resetCursor
} else {
ClearScreenAfterCursor
}
}
}
private[sbt] object ProgressState {
/**
* 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[sbt] def updateProgressState(
pe: ProgressEvent,
terminal: Terminal
): Unit = {
val state = terminal.progressState
val isRunning = terminal.prompt == Prompt.Running
val isBatch = terminal.prompt == Prompt.Batch
val isWatch = terminal.prompt == Prompt.Watch
val noPrompt = terminal.prompt == Prompt.NoPrompt
if (terminal.isSupershellEnabled) {
if (!pe.skipIfActive.getOrElse(false) || (!isRunning && !isBatch)) {
terminal.withPrintStream { ps =>
val info =
if ((isRunning || isBatch || noPrompt) && pe.channelName
.fold(true)(_ == terminal.name)) {
pe.items.map { item =>
val elapsed = item.elapsedMicros / 1000000L
s" | => ${item.name} ${elapsed}s"
}
} else {
pe.command.toSeq.flatMap { cmd =>
val tail = if (isWatch) Nil else "enter 'cancel' to stop evaluation" :: Nil
s"sbt server is running '$cmd'" :: tail
}
}
val currentLength = info.foldLeft(0)(_ + terminal.lineCount(_))
val previousLines = state.progressLines.getAndSet(info)
val prevLength = previousLines.foldLeft(0)(_ + terminal.lineCount(_))
val lastLine = terminal.prompt match {
case Prompt.Running | Prompt.Batch => terminal.getLastLine.getOrElse("")
case a => a.render()
}
val prevSize = prevLength + state.padding.get
val newPadding = math.max(0, prevSize - currentLength)
state.padding.set(newPadding)
state.printPrompt(terminal, ps)
ps.print(state.printProgress(terminal, lastLine))
ps.flush()
}
} else if (state.progressLines.get.nonEmpty) {
state.progressLines.set(Nil)
terminal.withPrintStream { ps =>
val lastLine = terminal.getLastLine.getOrElse("")
ps.print(lastLine + ClearScreenAfterCursor)
ps.flush()
}
}
}
}
}