From adc8d5ee6e29bb5b918be986fd9eb9cae8157e57 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Mon, 10 Aug 2020 11:02:02 -0700 Subject: [PATCH] Add reprompt fast track command With the latest sbt snapshot, the ui would get stuck if the user entered an empty command. They would be presented with an empty prompt and could not input any commands. This was caused by the change in d569abe70ae9b5e95fb0d699fb01ffd492d9345e that reset the prompt after a line was read. I had tried to optimize line reading by ignoring empty commands in UITask.readline so we wouldn't have to make a new thread. This optimization wasn't really buying much since it only affects how quickly the user is reprompted after entering an empty command. Unless a user is spamming the key, they shouldn't notice a difference. --- .../src/main/scala/sbt/internal/util/LineReader.scala | 6 +++--- main-command/src/main/scala/sbt/internal/ui/UITask.scala | 9 +++++---- main/src/main/scala/sbt/internal/CommandExchange.scala | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) 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 4b0235572..a963582f4 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 @@ -74,13 +74,13 @@ object LineReader { parser: Parser[_], terminal: Terminal, ): LineReader = { - val term = JLine3(terminal) // We may want to consider insourcing LineReader.java from jline. We don't otherwise // directly need jline3 for sbt. - val reader = LineReaderBuilder.builder().terminal(term).completer(completer(parser)).build() - historyPath.foreach(f => reader.setVariable(JLineReader.HISTORY_FILE, f)) new LineReader { override def readLine(prompt: String, mask: Option[Char]): Option[String] = { + val term = JLine3(terminal) + val reader = LineReaderBuilder.builder().terminal(term).completer(completer(parser)).build() + historyPath.foreach(f => reader.setVariable(JLineReader.HISTORY_FILE, f)) try terminal.withRawInput { Option(mask.map(reader.readLine(prompt, _)).getOrElse(reader.readLine(prompt))) } catch { 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 8fd4d4177..f47def3a6 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UITask.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UITask.scala @@ -59,7 +59,7 @@ private[sbt] object UITask { def readLine(): Either[String, String] = try { val clear = terminal.ansi(ClearPromptLine, "") - @tailrec def impl(): Either[String, String] = { + val res = { val thread = Thread.currentThread if (thread.isInterrupted || closed.get) throw interrupted val reader = LineReader.createReader(history(state), parser, terminal) @@ -76,16 +76,17 @@ private[sbt] object UITask { case None => Left(TerminateAction) case Some(s: String) => s.trim() match { - case "" => impl() + // We need to put the empty string on the fast track queue so that we can + // reprompt the user if another command is running on the server. + case "" => Left("") case cmd @ (`Shutdown` | `TerminateAction` | `Cancel`) => Left(cmd) case cmd => Right(cmd) } } } - val res = impl() terminal.setPrompt(Prompt.Pending) res - } catch { case e: InterruptedException => Right("") } + } catch { case e: InterruptedException => Left("") } override def close(): Unit = closed.set(true) } } diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 505c913c2..b2d077a4c 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -446,7 +446,7 @@ private[sbt] final class CommandExchange { case null => case mt: FastTrackTask => mt.task match { - case `attach` => mt.channel.prompt(ConsolePromptEvent(lastState.get)) + case `attach` | "" => mt.channel.prompt(ConsolePromptEvent(lastState.get)) case `Cancel` => Option(currentExecRef.get).foreach(cancel) mt.channel.prompt(ConsolePromptEvent(lastState.get))