mirror of https://github.com/sbt/sbt.git
Add super shell options
This commit adds a few options to supershell: 1. Max items -- sets the max number of tasks to display in the progress reports. It is pretty hard to read more than a few items in the progress reports so I set the default limit to 8 and made that configurable via the superShellMaxTasks parameter. If there are more than the limit, there is an additional line telling how many additional tasks are running 2. sleep -- sets how long to sleep between reports. The default is 500ms to ensure that it updates at least once per second but the previous value of 100ms is more frequent than necessary 3. threshold -- sets the minimum duration a task has to run before being printed in the progress reports. The default threshold is increased from 10ms to 100ms. This introduces a delay of threshold milliseconds before any progress lines appear and also means that if no tasks ever exceed the threshold, then no progress is ever displayed.
This commit is contained in:
parent
102e3d1969
commit
d58aab5d84
|
|
@ -26,14 +26,16 @@ private[sbt] final class ProgressState(
|
|||
val padding: AtomicInteger,
|
||||
val blankZone: Int,
|
||||
val currentLineBytes: AtomicReference[ArrayBuffer[Byte]],
|
||||
val maxItems: Int,
|
||||
) {
|
||||
def this(blankZone: Int) =
|
||||
this(
|
||||
new AtomicReference(Nil),
|
||||
new AtomicInteger(0),
|
||||
blankZone,
|
||||
new AtomicReference(new ArrayBuffer[Byte]),
|
||||
)
|
||||
def this(blankZone: Int, maxItems: Int) = this(
|
||||
new AtomicReference(Nil),
|
||||
new AtomicInteger(0),
|
||||
blankZone,
|
||||
new AtomicReference(new ArrayBuffer[Byte]),
|
||||
maxItems,
|
||||
)
|
||||
def this(blankZone: Int) = this(blankZone, 8)
|
||||
def currentLine: Option[String] =
|
||||
new String(currentLineBytes.get.toArray, "UTF-8").linesIterator.toSeq.lastOption
|
||||
.map(EscHelpers.stripColorsAndMoves)
|
||||
|
|
@ -162,14 +164,18 @@ private[sbt] object ProgressState {
|
|||
terminal.withPrintStream { ps =>
|
||||
val commandFromThisTerminal = pe.channelName.fold(true)(_ == terminal.name)
|
||||
val info = if (commandFromThisTerminal) {
|
||||
pe.items.map { item =>
|
||||
val base = pe.items.map { item =>
|
||||
val elapsed = item.elapsedMicros / 1000000L
|
||||
s" | => ${item.name} ${elapsed}s"
|
||||
}
|
||||
val limit = state.maxItems
|
||||
if (base.size > limit)
|
||||
s" | ... (${base.size - limit} other tasks)" +: base.takeRight(limit)
|
||||
else base
|
||||
} else {
|
||||
pe.command.toSeq.flatMap { cmd =>
|
||||
val width = terminal.getWidth
|
||||
val sanitized = if ((cmd.length + SERVER_IS_RUNNING_LENGTH) < width) {
|
||||
val sanitized = if ((cmd.length + SERVER_IS_RUNNING_LENGTH) > width) {
|
||||
if (SERVER_IS_RUNNING_LENGTH + cmd.length < width) cmd
|
||||
else cmd.take(MIN_COMMAND_WIDTH) + "..."
|
||||
} else cmd
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ trait Terminal extends AutoCloseable {
|
|||
private[sbt] def withPrintStream[T](f: PrintStream => T): T
|
||||
private[sbt] def withRawOutput[R](f: => R): R
|
||||
private[sbt] def restore(): Unit = {}
|
||||
private[sbt] val progressState = new ProgressState(1)
|
||||
private[sbt] def progressState: ProgressState
|
||||
private[this] val promptHolder: AtomicReference[Prompt] = new AtomicReference(Prompt.Pending)
|
||||
private[sbt] final def prompt: Prompt = promptHolder.get
|
||||
private[sbt] final def setPrompt(newPrompt: Prompt): Unit =
|
||||
|
|
@ -315,6 +315,7 @@ object Terminal {
|
|||
|
||||
private[this] object ProxyTerminal extends Terminal {
|
||||
private def t: Terminal = activeTerminal.get
|
||||
override private[sbt] def progressState: ProgressState = t.progressState
|
||||
override def getWidth: Int = t.getWidth
|
||||
override def getHeight: Int = t.getHeight
|
||||
override def getLineHeightAndWidth(line: String): (Int, Int) = t.getLineHeightAndWidth(line)
|
||||
|
|
@ -755,6 +756,9 @@ object Terminal {
|
|||
|
||||
private val capabilityMap =
|
||||
org.jline.utils.InfoCmp.Capability.values().map(c => c.toString -> c).toMap
|
||||
private val consoleProgressState = new AtomicReference[ProgressState](new ProgressState(1))
|
||||
private[sbt] def setConsoleProgressState(progressState: ProgressState): Unit =
|
||||
consoleProgressState.set(progressState)
|
||||
|
||||
@deprecated("For compatibility only", "1.4.0")
|
||||
private[sbt] def deprecatedTeminal: jline.Terminal = console.toJLine
|
||||
|
|
@ -770,6 +774,7 @@ object Terminal {
|
|||
}
|
||||
private[this] val isCI = sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI")
|
||||
override lazy val isAnsiSupported: Boolean = term.isAnsiSupported && !isCI
|
||||
override private[sbt] def progressState: ProgressState = consoleProgressState.get
|
||||
override def isEchoEnabled: Boolean = system.echo()
|
||||
override def isSuccessEnabled: Boolean = true
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean =
|
||||
|
|
@ -912,6 +917,7 @@ object Terminal {
|
|||
new WriteableInputStream(nullInputStream, "null-writeable-input-stream")
|
||||
private[sbt] val NullTerminal = new Terminal {
|
||||
override def close(): Unit = {}
|
||||
override private[sbt] def progressState: ProgressState = new ProgressState(1)
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean = false
|
||||
override def getHeight: Int = 0
|
||||
override def getLastLine: Option[String] = None
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ import sjsonnew._
|
|||
import sjsonnew.support.scalajson.unsafe.Converter
|
||||
|
||||
import scala.collection.immutable.ListMap
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.control.NonFatal
|
||||
import scala.xml.NodeSeq
|
||||
|
||||
|
|
@ -393,11 +393,15 @@ object Defaults extends BuildCommon {
|
|||
else appConfiguration.value.provider.scalaProvider.launcher.topLoader.getParent
|
||||
},
|
||||
useSuperShell := { if (insideCI.value) false else Terminal.console.isSupershellEnabled },
|
||||
superShellThreshold :== SysProp.supershellThreshold,
|
||||
superShellMaxTasks :== SysProp.supershellMaxTasks,
|
||||
superShellSleep :== SysProp.supershellSleep.millis,
|
||||
progressReports := {
|
||||
val rs = EvaluateTask.taskTimingProgress.toVector ++ EvaluateTask.taskTraceEvent.toVector
|
||||
rs map { Keys.TaskProgress(_) }
|
||||
},
|
||||
progressState := Some(new ProgressState(SysProp.supershellBlankZone)),
|
||||
// progressState is deprecated
|
||||
SettingKey[Option[ProgressState]]("progressState") := None,
|
||||
Previous.cache := new Previous(
|
||||
Def.streamsManagerKey.value,
|
||||
Previous.references.value.getReferences
|
||||
|
|
|
|||
|
|
@ -557,9 +557,13 @@ object Keys {
|
|||
private[sbt] val currentTaskProgress = AttributeKey[TaskProgress]("current-task-progress")
|
||||
private[sbt] val taskProgress = AttributeKey[sbt.internal.TaskProgress]("active-task-progress")
|
||||
val useSuperShell = settingKey[Boolean]("Enables (true) or disables the super shell.")
|
||||
val superShellMaxTasks = settingKey[Int]("The max number of tasks to display in the supershell progress report")
|
||||
val superShellSleep = settingKey[FiniteDuration]("The minimum duration to sleep between progress reports")
|
||||
val superShellThreshold = settingKey[FiniteDuration]("The minimum amount of time a task must be running to appear in the supershell progress report")
|
||||
val turbo = settingKey[Boolean]("Enables (true) or disables optional performance features.")
|
||||
// This key can be used to add custom ExecuteProgress instances
|
||||
val progressReports = settingKey[Seq[TaskProgress]]("A function that returns a list of progress reporters.").withRank(DTask)
|
||||
@deprecated("unused", "1.4.0")
|
||||
private[sbt] val progressState = settingKey[Option[ProgressState]]("The optional progress state if supershell is enabled.").withRank(Invisible)
|
||||
private[sbt] val postProgressReports = settingKey[Unit]("Internally used to modify logger.").withRank(DTask)
|
||||
@deprecated("No longer used", "1.3.0")
|
||||
|
|
|
|||
|
|
@ -935,13 +935,25 @@ object BuiltinCommands {
|
|||
// This is a workaround for the console task in dotty which uses the classloader cache.
|
||||
// We need to override the top loader in that case so that it gets the forked jline.
|
||||
s5.extendedClassLoaderCache.setParent(Project.extract(s5).get(Keys.scalaInstanceTopLoader))
|
||||
CheckBuildSources.init(LintUnused.lintUnusedFunc(s5))
|
||||
addSuperShellParams(CheckBuildSources.init(LintUnused.lintUnusedFunc(s5)))
|
||||
}
|
||||
|
||||
private val setupGlobalFileTreeRepository: State => State = { state =>
|
||||
state.get(sbt.nio.Keys.globalFileTreeRepository).foreach(_.close())
|
||||
state.put(sbt.nio.Keys.globalFileTreeRepository, FileTreeRepository.default)
|
||||
}
|
||||
private val addSuperShellParams: State => State = (s: State) => {
|
||||
val extracted = Project.extract(s)
|
||||
import scala.concurrent.duration._
|
||||
val sleep = extracted.getOpt(Keys.superShellSleep).getOrElse(SysProp.supershellSleep.millis)
|
||||
val threshold =
|
||||
extracted.getOpt(Keys.superShellThreshold).getOrElse(SysProp.supershellThreshold)
|
||||
val maxItems = extracted.getOpt(Keys.superShellMaxTasks).getOrElse(SysProp.supershellMaxTasks)
|
||||
Terminal.setConsoleProgressState(new ProgressState(1, maxItems))
|
||||
s.put(Keys.superShellSleep.key, sleep)
|
||||
.put(Keys.superShellThreshold.key, threshold)
|
||||
.put(Keys.superShellMaxTasks.key, maxItems)
|
||||
}
|
||||
private val addCacheStoreFactoryFactory: State => State = (s: State) => {
|
||||
val size = Project
|
||||
.extract(s)
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@ import sbt.protocol._
|
|||
import sbt.util.{ Logger, LoggerContext }
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.control.NonFatal
|
||||
import sbt.internal.FastTrackCommands
|
||||
import sbt.internal.SysProp
|
||||
|
||||
object MainLoop {
|
||||
|
||||
|
|
@ -150,7 +152,11 @@ object MainLoop {
|
|||
|
||||
def next(state: State): State = {
|
||||
val context = LoggerContext(useLog4J = state.get(Keys.useLog4J.key).getOrElse(false))
|
||||
val taskProgress = new TaskProgress
|
||||
val superShellSleep =
|
||||
state.get(Keys.superShellSleep.key).getOrElse(SysProp.supershellSleep.millis)
|
||||
val superShellThreshold =
|
||||
state.get(Keys.superShellThreshold.key).getOrElse(SysProp.supershellThreshold)
|
||||
val taskProgress = new TaskProgress(superShellSleep, superShellThreshold)
|
||||
try {
|
||||
ErrorHandling.wideConvert {
|
||||
state
|
||||
|
|
|
|||
|
|
@ -201,7 +201,8 @@ private[sbt] final class CommandExchange {
|
|||
instance,
|
||||
handlers,
|
||||
s.log,
|
||||
mkAskUser(name)
|
||||
mkAskUser(name),
|
||||
Option(lastState.get),
|
||||
)
|
||||
subscribe(channel)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,9 @@ object SysProp {
|
|||
def dumbTerm: Boolean = sys.env.get("TERM").contains("dumb")
|
||||
def supershell: Boolean = booleanOpt("sbt.supershell").getOrElse(!dumbTerm && color)
|
||||
|
||||
def supershellSleep: Long = long("sbt.supershell.sleep", 100L)
|
||||
def supershellMaxTasks: Int = int("sbt.supershell.maxitems", 8)
|
||||
def supershellSleep: Long = long("sbt.supershell.sleep", 500.millis.toMillis)
|
||||
def supershellThreshold: FiniteDuration = long("sbt.supershell.threshold", 100L).millis
|
||||
def supershellBlankZone: Int = int("sbt.supershell.blankzone", 1)
|
||||
|
||||
def defaultUseCoursier: Boolean = {
|
||||
|
|
|
|||
|
|
@ -20,13 +20,11 @@ import java.util.concurrent.{ ConcurrentHashMap, Executors, TimeoutException }
|
|||
/**
|
||||
* implements task progress display on the shell.
|
||||
*/
|
||||
private[sbt] class TaskProgress
|
||||
private[sbt] class TaskProgress(sleepDuration: FiniteDuration, threshold: FiniteDuration)
|
||||
extends AbstractTaskExecuteProgress
|
||||
with ExecuteProgress[Task]
|
||||
with AutoCloseable {
|
||||
private[this] val lastTaskCount = new AtomicInteger(0)
|
||||
private[this] val sleepDuration = SysProp.supershellSleep.millis
|
||||
private[this] val threshold = 10.millis
|
||||
private[this] val reportLoop = new AtomicReference[AutoCloseable]
|
||||
private[this] val active = new ConcurrentHashMap[Task[_], AutoCloseable]
|
||||
private[this] val nextReport = new AtomicReference(Deadline.now)
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
|
|||
|
||||
import BasicJsonProtocol._
|
||||
import Serialization.{ attach, promptChannel }
|
||||
import sbt.internal.util.ProgressState
|
||||
|
||||
final class NetworkChannel(
|
||||
val name: String,
|
||||
|
|
@ -58,7 +59,8 @@ final class NetworkChannel(
|
|||
instance: ServerInstance,
|
||||
handlers: Seq[ServerHandler],
|
||||
val log: Logger,
|
||||
mkUIThreadImpl: (State, CommandChannel) => UITask
|
||||
mkUIThreadImpl: (State, CommandChannel) => UITask,
|
||||
state: Option[State],
|
||||
) extends CommandChannel { self =>
|
||||
def this(
|
||||
name: String,
|
||||
|
|
@ -77,7 +79,8 @@ final class NetworkChannel(
|
|||
instance,
|
||||
handlers,
|
||||
log,
|
||||
new UITask.AskUserTask(_, _)
|
||||
new UITask.AskUserTask(_, _),
|
||||
None
|
||||
)
|
||||
|
||||
private val running = new AtomicBoolean(true)
|
||||
|
|
@ -787,6 +790,10 @@ final class NetworkChannel(
|
|||
)
|
||||
}
|
||||
private[this] val blockedThreads = ConcurrentHashMap.newKeySet[Thread]
|
||||
override private[sbt] val progressState: ProgressState = new ProgressState(
|
||||
1,
|
||||
state.flatMap(_.get(Keys.superShellMaxTasks.key)).getOrElse(SysProp.supershellMaxTasks)
|
||||
)
|
||||
override def getWidth: Int = getProperty(_.width, 0).getOrElse(0)
|
||||
override def getHeight: Int = getProperty(_.height, 0).getOrElse(0)
|
||||
override def isAnsiSupported: Boolean = getProperty(_.isAnsiSupported, false).getOrElse(false)
|
||||
|
|
|
|||
Loading…
Reference in New Issue