Merge pull request #4454 from eed3si9n/wip/prompt-fix

Fix prompt for task progress
This commit is contained in:
eugene yokota 2018-11-29 10:47:30 -05:00 committed by GitHub
commit 34ef5ab500
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 34 deletions

View File

@ -216,7 +216,7 @@ val completeProj = (project in file("internal") / "util-complete")
exclude[DirectMissingMethodProblem]("sbt.internal.util.complete.History.this"),
),
)
.configure(addSbtIO, addSbtUtilControl)
.configure(addSbtIO, addSbtUtilControl, addSbtUtilLogging)
// A logic with restricted negation as failure for a unique, stable model
val logicProj = (project in file("internal") / "util-logic")

View File

@ -55,25 +55,40 @@ abstract class JLine extends LineReader {
else
readLineDirectRaw(prompt, mask)
private[this] val console = ConsoleOut.systemOut
private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] = {
val newprompt = handleMultilinePrompt(prompt)
mask match {
val result = mask match {
case Some(m) => Option(reader.readLine(newprompt, m))
case None => Option(reader.readLine(newprompt))
}
// since the task progress scrolls the logs upward, and expects the cursors to be on
// the last text line, this moves the cursor up to complensate for user hitting return.
val CursorUp1 = s"\u001B[A"
if (ConsoleAppender.showProgress) console.print(CursorUp1)
else ()
result
}
private[this] def handleMultilinePrompt(prompt: String): String = {
val lines = """\r?\n""".r.split(prompt)
lines.length match {
case 0 | 1 => prompt
case _ =>
val lines0 = """\r?\n""".r.split(prompt)
lines0.length match {
case 0 | 1 => handleProgress(prompt)
case _ =>
val lines = lines0.toList map handleProgress
// Workaround for regression jline/jline2#205
reader.getOutput.write(lines.init.mkString("\n") + "\n")
lines.last
}
}
private[this] def handleProgress(prompt: String): String = {
import ConsoleAppender._
if (showProgress) s"$ScrollUp$DeleteLine" + prompt
else prompt
}
private[this] def resume(): Unit = {
jline.TerminalFactory.reset
JLine.terminal.init

View File

@ -77,8 +77,6 @@ object Aggregation {
if (show.taskValues) printSettings(r, show.print)
}
if (show.success) printSuccess(start, stop, extracted, success, log)
// wait for async logger to catch up
Thread.sleep(100)
}
def timedRun[T](
@ -131,9 +129,9 @@ object Aggregation {
if (get(showSuccess)) {
if (get(showTiming)) {
val msg = timingString(start, stop, structure.data, currentRef)
if (success) log.success(msg + "\n") else log.error(msg + "\n")
if (success) log.success(msg) else log.error(msg)
} else if (success)
log.success("" + "\n")
log.success("")
}
}

View File

@ -11,7 +11,7 @@ package internal
import sbt.internal.util.{ RMap, ConsoleOut }
import java.util.concurrent.ConcurrentHashMap
import scala.concurrent.{ blocking, Future, ExecutionContext }
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
import scala.collection.JavaConverters._
import scala.collection.concurrent.TrieMap
import TaskProgress._
@ -29,6 +29,7 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
private[this] val calledBy = new ConcurrentHashMap[Task[_], Task[_]]
private[this] val anonOwners = new ConcurrentHashMap[Task[_], Task[_]]
private[this] val isReady = new AtomicBoolean(false)
private[this] val lastTaskCount = new AtomicInteger(0)
private[this] val isAllCompleted = new AtomicBoolean(false)
override def initial: Unit = ()
@ -55,8 +56,9 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
}
override def workFinished[A](task: Task[A], result: Either[Task[A], Result[A]]): Unit = {
val start = activeTasks.get(task)
timings.put(task, System.nanoTime - start)
activeTasks.remove(task)
timings.put(task, System.nanoTime - activeTasks.get(task))
// we need this to infer anonymous task names
result.left.foreach { t =>
calledBy.put(t, task)
@ -64,9 +66,6 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
}
override def completed[A](state: Unit, task: Task[A], result: Result[A]): Unit = ()
override def allCompleted(state: Unit, results: RMap[Task, Result]): Unit = {
isAllCompleted.set(true)
}
import ExecutionContext.Implicits._
Future {
@ -75,7 +74,6 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
Thread.sleep(500)
}
}
readyLog()
while (!isAllCompleted.get) {
blocking {
report()
@ -85,34 +83,65 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
}
private[this] val console = ConsoleOut.systemOut
private[this] def readyLog(): Unit = {
console.println("")
console.println("")
console.println("")
console.println("")
console.println("")
console.println("")
console.print(CursorUp5)
override def allCompleted(state: Unit, results: RMap[Task, Result]): Unit = {
isAllCompleted.set(true)
// completionReport()
}
private[this] val stopReportTask =
private[this] val skipReportTasks =
Set("run", "bgRun", "fgRun", "scala", "console", "consoleProject")
private[this] def report(): Unit = console.lockObject.synchronized {
val currentTasks = activeTasks.asScala.toList
def report0: Unit = {
val ltc = lastTaskCount.get
val currentTasksCount = currentTasks.size
def report0(): Unit = {
console.print(s"$CursorDown1")
currentTasks foreach {
case (task, start) =>
val elapsed = (System.nanoTime - start) / 1000000000L
console.println(s"$DeleteLine | => ${taskName(task)} ${elapsed}s")
}
console.print(cursorUp(currentTasks.size + 1))
if (ltc > currentTasksCount) deleteConsoleLines(ltc - currentTasksCount)
else ()
console.print(cursorUp(math.max(currentTasksCount, ltc) + 1))
}
val isStop = currentTasks
if (containsSkipTasks(currentTasks)) ()
else report0()
lastTaskCount.set(currentTasksCount)
}
// todo: use logger instead of console
// private[this] def completionReport(): Unit = console.lockObject.synchronized {
// val completedTasks = timings.asScala.toList
// val notableTasks = completedTasks
// .filter({
// case (_, time: Long) => time >= 1000000000L * 10L
// })
// .sortBy({
// case (_, time: Long) => -time
// })
// .take(5)
// def report0(): Unit = {
// console.print(s"$CursorDown1")
// console.println(s"$DeleteLine notable completed tasks:")
// notableTasks foreach {
// case (task, time) =>
// val elapsed = time / 1000000000L
// console.println(s"$DeleteLine | => ${taskName(task)} ${elapsed}s")
// }
// }
// if (containsSkipTasks(notableTasks) || notableTasks.isEmpty) ()
// else report0()
// }
private[this] def containsSkipTasks(tasks: List[(Task[_], Long)]): Boolean =
tasks
.map({ case (t, _) => taskName(t) })
.exists(n => stopReportTask.exists(m => n.endsWith("/ " + m)))
if (isStop) ()
else report0
.exists(n => skipReportTasks.exists(m => n.endsWith("/ " + m)))
private[this] def deleteConsoleLines(n: Int): Unit = {
(1 to n) foreach { _ =>
console.println(s"$DeleteLine")
}
}
private[this] val taskNameCache = TrieMap.empty[Task[_], String]
@ -130,7 +159,6 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
private[sbt] object TaskProgress {
final val DeleteLine = "\u001B[2K"
final val CursorUp5 = cursorUp(5)
def cursorUp(n: Int): String = s"\u001B[${n}A"
def cursorDown(n: Int): String = s"\u001B[${n}B"
final val CursorDown1 = cursorDown(1)

View File

@ -10,7 +10,7 @@ object Dependencies {
// sbt modules
private val ioVersion = "1.3.0-M3"
private val utilVersion = "1.3.0-M2"
private val utilVersion = "1.3.0-M3"
private val lmVersion =
sys.props.get("sbt.build.lm.version") match {
case Some(version) => version