diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index 52a36ca8d..4b0235572 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -73,7 +73,6 @@ object LineReader { historyPath: Option[File], parser: Parser[_], terminal: Terminal, - prompt: Prompt = Prompt.Running, ): LineReader = { val term = JLine3(terminal) // We may want to consider insourcing LineReader.java from jline. We don't otherwise diff --git a/main-command/src/main/scala/sbt/internal/ui/UITask.scala b/main-command/src/main/scala/sbt/internal/ui/UITask.scala index dce4b1012..8fd4d4177 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UITask.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UITask.scala @@ -82,7 +82,9 @@ private[sbt] object UITask { } } } - impl() + val res = impl() + terminal.setPrompt(Prompt.Pending) + res } catch { case e: InterruptedException => Right("") } override def close(): Unit = closed.set(true) } diff --git a/main-command/src/main/scala/sbt/internal/ui/UserThread.scala b/main-command/src/main/scala/sbt/internal/ui/UserThread.scala index 7f1d007f5..fd6d45778 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UserThread.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UserThread.scala @@ -31,21 +31,21 @@ private[sbt] class UserThread(val channel: CommandChannel) extends AutoCloseable uiThread.synchronized { val task = channel.makeUIThread(state) def submit(): Thread = { - def close(): Unit = { - uiThread.get match { - case (_, t) if t == thread => uiThread.set(null) - case _ => - } + val thread: Thread = new Thread(s"sbt-$name-ui-thread") { + setDaemon(true) + override def run(): Unit = + try task.run() + finally uiThread.get match { + case (_, t) if t == this => uiThread.set(null) + case _ => + } } - lazy val thread = new Thread(() => { - try task.run() - finally close() - }, s"sbt-$name-ui-thread") - thread.setDaemon(true) - thread.start() uiThread.getAndSet((task, thread)) match { - case null => - case (_, t) => t.interrupt() + case null => thread.start() + case (task, t) if t.getClass != task.getClass => + stopThreadImpl() + thread.start() + case t => uiThread.set(t) } thread } @@ -53,14 +53,14 @@ private[sbt] class UserThread(val channel: CommandChannel) extends AutoCloseable case null => uiThread.set((task, submit())) case (t, _) if t.getClass == task.getClass => case (t, thread) => - thread.interrupt() + stopThreadImpl() uiThread.set((task, submit())) } } Option(lastProgressEvent.get).foreach(onProgressEvent) } - private[sbt] def stopThread(): Unit = uiThread.synchronized { + private[sbt] def stopThreadImpl(): Unit = uiThread.synchronized { uiThread.getAndSet(null) match { case null => case (t, thread) => @@ -75,21 +75,25 @@ private[sbt] class UserThread(val channel: CommandChannel) extends AutoCloseable () } } + private[sbt] def stopThread(): Unit = uiThread.synchronized(stopThreadImpl()) - private[sbt] def onConsolePromptEvent(consolePromptEvent: ConsolePromptEvent): Unit = { - channel.terminal.withPrintStream { ps => - ps.print(ConsoleAppender.ClearScreenAfterCursor) - ps.flush() + private[sbt] def onConsolePromptEvent(consolePromptEvent: ConsolePromptEvent): Unit = + // synchronize to ensure that the state isn't modified during the call to reset + // at the bottom + synchronized { + channel.terminal.withPrintStream { ps => + ps.print(ConsoleAppender.ClearScreenAfterCursor) + ps.flush() + } + val state = consolePromptEvent.state + terminal.prompt match { + case Prompt.Running | Prompt.Pending => + terminal.setPrompt(Prompt.AskUser(() => UITask.shellPrompt(terminal, state))) + case _ => + } + onProgressEvent(ProgressEvent("Info", Vector(), None, None, None)) + reset(state) } - val state = consolePromptEvent.state - terminal.prompt match { - case Prompt.Running | Prompt.Pending => - terminal.setPrompt(Prompt.AskUser(() => UITask.shellPrompt(terminal, state))) - case _ => - } - onProgressEvent(ProgressEvent("Info", Vector(), None, None, None)) - reset(state) - } private[sbt] def onConsoleUnpromptEvent( consoleUnpromptEvent: ConsoleUnpromptEvent diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index 0d2f57c2e..de8b5dfb7 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -15,8 +15,8 @@ import sbt.internal.ShutdownHooks import sbt.internal.langserver.ErrorCodes import sbt.internal.protocol.JsonRpcResponseError import sbt.internal.nio.CheckBuildSources.CheckBuildSourcesKey -import sbt.internal.util.{ ErrorHandling, GlobalLogBacking, Terminal } -import sbt.internal.{ ConsoleUnpromptEvent, ShutdownHooks } +import sbt.internal.util.{ ErrorHandling, GlobalLogBacking, Prompt, Terminal } +import sbt.internal.ShutdownHooks import sbt.io.{ IO, Using } import sbt.protocol._ import sbt.util.{ Logger, LoggerContext } @@ -206,13 +206,16 @@ object MainLoop { state.put(sbt.Keys.currentTaskProgress, new Keys.TaskProgress(progress)) } else state } - StandardMain.exchange.setState(progressState) - StandardMain.exchange.setExec(Some(exec)) - StandardMain.exchange.unprompt(ConsoleUnpromptEvent(exec.source)) + exchange.setState(progressState) + exchange.setExec(Some(exec)) val restoreTerminal = channelName.flatMap(exchange.channelForName) match { case Some(c) => val prevTerminal = Terminal.set(c.terminal) + val prevPrompt = c.terminal.prompt + // temporarily set the prompt to running during task evaluation + c.terminal.setPrompt(Prompt.Running) () => { + c.terminal.setPrompt(prevPrompt) Terminal.set(prevTerminal) c.terminal.flush() } diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 3a5eaea79..6cb4f4ece 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -133,17 +133,10 @@ private[sbt] final class CommandExchange { } } // Do not manually run GC until the user has been idling for at least the min gc interval. - val exec = impl(interval match { + impl(interval match { case d: FiniteDuration => Some(d.fromNow) case _ => None }, idleDeadline) - exec.source.foreach { s => - channelForName(s.channelName).foreach { - case c if c.terminal.prompt != Prompt.Batch => c.terminal.setPrompt(Prompt.Running) - case _ => - } - } - exec } private def addConsoleChannel(): Unit =