From ed4d40d3e2db0b5fdfdefb7bf989039bf3820a92 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Tue, 7 Jul 2020 14:06:55 -0700 Subject: [PATCH] Move ProgressState into its own file This didn't really belong in ConsoleAppender.scala anymore. --- .../sbt/internal/util/ConsoleAppender.scala | 139 +--------------- .../sbt/internal/util/ProgressState.scala | 157 ++++++++++++++++++ 2 files changed, 158 insertions(+), 138 deletions(-) create mode 100644 internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala index ddc2cb647..c89a51204 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala @@ -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() - } - } - } - } -} diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala new file mode 100644 index 000000000..13332812b --- /dev/null +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala @@ -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() + } + } + } + } +}