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 padding: AtomicInteger,
|
||||||
val blankZone: Int,
|
val blankZone: Int,
|
||||||
val currentLineBytes: AtomicReference[ArrayBuffer[Byte]],
|
val currentLineBytes: AtomicReference[ArrayBuffer[Byte]],
|
||||||
|
val maxItems: Int,
|
||||||
) {
|
) {
|
||||||
def this(blankZone: Int) =
|
def this(blankZone: Int, maxItems: Int) = this(
|
||||||
this(
|
|
||||||
new AtomicReference(Nil),
|
new AtomicReference(Nil),
|
||||||
new AtomicInteger(0),
|
new AtomicInteger(0),
|
||||||
blankZone,
|
blankZone,
|
||||||
new AtomicReference(new ArrayBuffer[Byte]),
|
new AtomicReference(new ArrayBuffer[Byte]),
|
||||||
|
maxItems,
|
||||||
)
|
)
|
||||||
|
def this(blankZone: Int) = this(blankZone, 8)
|
||||||
def currentLine: Option[String] =
|
def currentLine: Option[String] =
|
||||||
new String(currentLineBytes.get.toArray, "UTF-8").linesIterator.toSeq.lastOption
|
new String(currentLineBytes.get.toArray, "UTF-8").linesIterator.toSeq.lastOption
|
||||||
.map(EscHelpers.stripColorsAndMoves)
|
.map(EscHelpers.stripColorsAndMoves)
|
||||||
|
|
@ -162,14 +164,18 @@ private[sbt] object ProgressState {
|
||||||
terminal.withPrintStream { ps =>
|
terminal.withPrintStream { ps =>
|
||||||
val commandFromThisTerminal = pe.channelName.fold(true)(_ == terminal.name)
|
val commandFromThisTerminal = pe.channelName.fold(true)(_ == terminal.name)
|
||||||
val info = if (commandFromThisTerminal) {
|
val info = if (commandFromThisTerminal) {
|
||||||
pe.items.map { item =>
|
val base = pe.items.map { item =>
|
||||||
val elapsed = item.elapsedMicros / 1000000L
|
val elapsed = item.elapsedMicros / 1000000L
|
||||||
s" | => ${item.name} ${elapsed}s"
|
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 {
|
} else {
|
||||||
pe.command.toSeq.flatMap { cmd =>
|
pe.command.toSeq.flatMap { cmd =>
|
||||||
val width = terminal.getWidth
|
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
|
if (SERVER_IS_RUNNING_LENGTH + cmd.length < width) cmd
|
||||||
else cmd.take(MIN_COMMAND_WIDTH) + "..."
|
else cmd.take(MIN_COMMAND_WIDTH) + "..."
|
||||||
} else cmd
|
} else cmd
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ trait Terminal extends AutoCloseable {
|
||||||
private[sbt] def withPrintStream[T](f: PrintStream => T): T
|
private[sbt] def withPrintStream[T](f: PrintStream => T): T
|
||||||
private[sbt] def withRawOutput[R](f: => R): R
|
private[sbt] def withRawOutput[R](f: => R): R
|
||||||
private[sbt] def restore(): Unit = {}
|
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[this] val promptHolder: AtomicReference[Prompt] = new AtomicReference(Prompt.Pending)
|
||||||
private[sbt] final def prompt: Prompt = promptHolder.get
|
private[sbt] final def prompt: Prompt = promptHolder.get
|
||||||
private[sbt] final def setPrompt(newPrompt: Prompt): Unit =
|
private[sbt] final def setPrompt(newPrompt: Prompt): Unit =
|
||||||
|
|
@ -315,6 +315,7 @@ object Terminal {
|
||||||
|
|
||||||
private[this] object ProxyTerminal extends Terminal {
|
private[this] object ProxyTerminal extends Terminal {
|
||||||
private def t: Terminal = activeTerminal.get
|
private def t: Terminal = activeTerminal.get
|
||||||
|
override private[sbt] def progressState: ProgressState = t.progressState
|
||||||
override def getWidth: Int = t.getWidth
|
override def getWidth: Int = t.getWidth
|
||||||
override def getHeight: Int = t.getHeight
|
override def getHeight: Int = t.getHeight
|
||||||
override def getLineHeightAndWidth(line: String): (Int, Int) = t.getLineHeightAndWidth(line)
|
override def getLineHeightAndWidth(line: String): (Int, Int) = t.getLineHeightAndWidth(line)
|
||||||
|
|
@ -755,6 +756,9 @@ object Terminal {
|
||||||
|
|
||||||
private val capabilityMap =
|
private val capabilityMap =
|
||||||
org.jline.utils.InfoCmp.Capability.values().map(c => c.toString -> c).toMap
|
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")
|
@deprecated("For compatibility only", "1.4.0")
|
||||||
private[sbt] def deprecatedTeminal: jline.Terminal = console.toJLine
|
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")
|
private[this] val isCI = sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI")
|
||||||
override lazy val isAnsiSupported: Boolean = term.isAnsiSupported && !isCI
|
override lazy val isAnsiSupported: Boolean = term.isAnsiSupported && !isCI
|
||||||
|
override private[sbt] def progressState: ProgressState = consoleProgressState.get
|
||||||
override def isEchoEnabled: Boolean = system.echo()
|
override def isEchoEnabled: Boolean = system.echo()
|
||||||
override def isSuccessEnabled: Boolean = true
|
override def isSuccessEnabled: Boolean = true
|
||||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean =
|
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean =
|
||||||
|
|
@ -912,6 +917,7 @@ object Terminal {
|
||||||
new WriteableInputStream(nullInputStream, "null-writeable-input-stream")
|
new WriteableInputStream(nullInputStream, "null-writeable-input-stream")
|
||||||
private[sbt] val NullTerminal = new Terminal {
|
private[sbt] val NullTerminal = new Terminal {
|
||||||
override def close(): Unit = {}
|
override def close(): Unit = {}
|
||||||
|
override private[sbt] def progressState: ProgressState = new ProgressState(1)
|
||||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean = false
|
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean = false
|
||||||
override def getHeight: Int = 0
|
override def getHeight: Int = 0
|
||||||
override def getLastLine: Option[String] = None
|
override def getLastLine: Option[String] = None
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ import sjsonnew._
|
||||||
import sjsonnew.support.scalajson.unsafe.Converter
|
import sjsonnew.support.scalajson.unsafe.Converter
|
||||||
|
|
||||||
import scala.collection.immutable.ListMap
|
import scala.collection.immutable.ListMap
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration._
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import scala.xml.NodeSeq
|
import scala.xml.NodeSeq
|
||||||
|
|
||||||
|
|
@ -393,11 +393,15 @@ object Defaults extends BuildCommon {
|
||||||
else appConfiguration.value.provider.scalaProvider.launcher.topLoader.getParent
|
else appConfiguration.value.provider.scalaProvider.launcher.topLoader.getParent
|
||||||
},
|
},
|
||||||
useSuperShell := { if (insideCI.value) false else Terminal.console.isSupershellEnabled },
|
useSuperShell := { if (insideCI.value) false else Terminal.console.isSupershellEnabled },
|
||||||
|
superShellThreshold :== SysProp.supershellThreshold,
|
||||||
|
superShellMaxTasks :== SysProp.supershellMaxTasks,
|
||||||
|
superShellSleep :== SysProp.supershellSleep.millis,
|
||||||
progressReports := {
|
progressReports := {
|
||||||
val rs = EvaluateTask.taskTimingProgress.toVector ++ EvaluateTask.taskTraceEvent.toVector
|
val rs = EvaluateTask.taskTimingProgress.toVector ++ EvaluateTask.taskTraceEvent.toVector
|
||||||
rs map { Keys.TaskProgress(_) }
|
rs map { Keys.TaskProgress(_) }
|
||||||
},
|
},
|
||||||
progressState := Some(new ProgressState(SysProp.supershellBlankZone)),
|
// progressState is deprecated
|
||||||
|
SettingKey[Option[ProgressState]]("progressState") := None,
|
||||||
Previous.cache := new Previous(
|
Previous.cache := new Previous(
|
||||||
Def.streamsManagerKey.value,
|
Def.streamsManagerKey.value,
|
||||||
Previous.references.value.getReferences
|
Previous.references.value.getReferences
|
||||||
|
|
|
||||||
|
|
@ -557,9 +557,13 @@ object Keys {
|
||||||
private[sbt] val currentTaskProgress = AttributeKey[TaskProgress]("current-task-progress")
|
private[sbt] val currentTaskProgress = AttributeKey[TaskProgress]("current-task-progress")
|
||||||
private[sbt] val taskProgress = AttributeKey[sbt.internal.TaskProgress]("active-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 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.")
|
val turbo = settingKey[Boolean]("Enables (true) or disables optional performance features.")
|
||||||
// This key can be used to add custom ExecuteProgress instances
|
// 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)
|
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 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)
|
private[sbt] val postProgressReports = settingKey[Unit]("Internally used to modify logger.").withRank(DTask)
|
||||||
@deprecated("No longer used", "1.3.0")
|
@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.
|
// 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.
|
// 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))
|
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 =>
|
private val setupGlobalFileTreeRepository: State => State = { state =>
|
||||||
state.get(sbt.nio.Keys.globalFileTreeRepository).foreach(_.close())
|
state.get(sbt.nio.Keys.globalFileTreeRepository).foreach(_.close())
|
||||||
state.put(sbt.nio.Keys.globalFileTreeRepository, FileTreeRepository.default)
|
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) => {
|
private val addCacheStoreFactoryFactory: State => State = (s: State) => {
|
||||||
val size = Project
|
val size = Project
|
||||||
.extract(s)
|
.extract(s)
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@ import sbt.protocol._
|
||||||
import sbt.util.{ Logger, LoggerContext }
|
import sbt.util.{ Logger, LoggerContext }
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
import scala.concurrent.duration._
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import sbt.internal.FastTrackCommands
|
import sbt.internal.FastTrackCommands
|
||||||
|
import sbt.internal.SysProp
|
||||||
|
|
||||||
object MainLoop {
|
object MainLoop {
|
||||||
|
|
||||||
|
|
@ -150,7 +152,11 @@ object MainLoop {
|
||||||
|
|
||||||
def next(state: State): State = {
|
def next(state: State): State = {
|
||||||
val context = LoggerContext(useLog4J = state.get(Keys.useLog4J.key).getOrElse(false))
|
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 {
|
try {
|
||||||
ErrorHandling.wideConvert {
|
ErrorHandling.wideConvert {
|
||||||
state
|
state
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,8 @@ private[sbt] final class CommandExchange {
|
||||||
instance,
|
instance,
|
||||||
handlers,
|
handlers,
|
||||||
s.log,
|
s.log,
|
||||||
mkAskUser(name)
|
mkAskUser(name),
|
||||||
|
Option(lastState.get),
|
||||||
)
|
)
|
||||||
subscribe(channel)
|
subscribe(channel)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,9 @@ object SysProp {
|
||||||
def dumbTerm: Boolean = sys.env.get("TERM").contains("dumb")
|
def dumbTerm: Boolean = sys.env.get("TERM").contains("dumb")
|
||||||
def supershell: Boolean = booleanOpt("sbt.supershell").getOrElse(!dumbTerm && color)
|
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 supershellBlankZone: Int = int("sbt.supershell.blankzone", 1)
|
||||||
|
|
||||||
def defaultUseCoursier: Boolean = {
|
def defaultUseCoursier: Boolean = {
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,11 @@ import java.util.concurrent.{ ConcurrentHashMap, Executors, TimeoutException }
|
||||||
/**
|
/**
|
||||||
* implements task progress display on the shell.
|
* implements task progress display on the shell.
|
||||||
*/
|
*/
|
||||||
private[sbt] class TaskProgress
|
private[sbt] class TaskProgress(sleepDuration: FiniteDuration, threshold: FiniteDuration)
|
||||||
extends AbstractTaskExecuteProgress
|
extends AbstractTaskExecuteProgress
|
||||||
with ExecuteProgress[Task]
|
with ExecuteProgress[Task]
|
||||||
with AutoCloseable {
|
with AutoCloseable {
|
||||||
private[this] val lastTaskCount = new AtomicInteger(0)
|
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 reportLoop = new AtomicReference[AutoCloseable]
|
||||||
private[this] val active = new ConcurrentHashMap[Task[_], AutoCloseable]
|
private[this] val active = new ConcurrentHashMap[Task[_], AutoCloseable]
|
||||||
private[this] val nextReport = new AtomicReference(Deadline.now)
|
private[this] val nextReport = new AtomicReference(Deadline.now)
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
|
||||||
|
|
||||||
import BasicJsonProtocol._
|
import BasicJsonProtocol._
|
||||||
import Serialization.{ attach, promptChannel }
|
import Serialization.{ attach, promptChannel }
|
||||||
|
import sbt.internal.util.ProgressState
|
||||||
|
|
||||||
final class NetworkChannel(
|
final class NetworkChannel(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|
@ -58,7 +59,8 @@ final class NetworkChannel(
|
||||||
instance: ServerInstance,
|
instance: ServerInstance,
|
||||||
handlers: Seq[ServerHandler],
|
handlers: Seq[ServerHandler],
|
||||||
val log: Logger,
|
val log: Logger,
|
||||||
mkUIThreadImpl: (State, CommandChannel) => UITask
|
mkUIThreadImpl: (State, CommandChannel) => UITask,
|
||||||
|
state: Option[State],
|
||||||
) extends CommandChannel { self =>
|
) extends CommandChannel { self =>
|
||||||
def this(
|
def this(
|
||||||
name: String,
|
name: String,
|
||||||
|
|
@ -77,7 +79,8 @@ final class NetworkChannel(
|
||||||
instance,
|
instance,
|
||||||
handlers,
|
handlers,
|
||||||
log,
|
log,
|
||||||
new UITask.AskUserTask(_, _)
|
new UITask.AskUserTask(_, _),
|
||||||
|
None
|
||||||
)
|
)
|
||||||
|
|
||||||
private val running = new AtomicBoolean(true)
|
private val running = new AtomicBoolean(true)
|
||||||
|
|
@ -787,6 +790,10 @@ final class NetworkChannel(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
private[this] val blockedThreads = ConcurrentHashMap.newKeySet[Thread]
|
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 getWidth: Int = getProperty(_.width, 0).getOrElse(0)
|
||||||
override def getHeight: Int = getProperty(_.height, 0).getOrElse(0)
|
override def getHeight: Int = getProperty(_.height, 0).getOrElse(0)
|
||||||
override def isAnsiSupported: Boolean = getProperty(_.isAnsiSupported, false).getOrElse(false)
|
override def isAnsiSupported: Boolean = getProperty(_.isAnsiSupported, false).getOrElse(false)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue