From 82d6a050de44943f03825786294987bd0a2a0311 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Tue, 20 Oct 2020 16:55:21 -0700 Subject: [PATCH] Handle ctrl+d in canonical mode In canonical mode, System.in will return -1 for ctrl+d on an empty line. The result of this behavior was that if a user entered ctrl+d during run in a task that was reading from System.in, sbt would end up exiting whenever the task exited. This happened because the WriteableInputStream would close itself when it read -1 from the input stream, which it assumed meant that the underlying input stream itself had been closed. When the jline reader tried to read from the closed WriteableInputStream, it would throw an exception and if the line reader was for the console channel, it would be interpreted as the user had inputted ctrl+d in the sbt shell which is supposed to exit sbt. This change fixes that behavior so that sbt can continue reading input after the run task exits. --- .../main/scala/sbt/internal/util/Terminal.scala | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala index 7f75f733b..03ed8290c 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala @@ -450,12 +450,16 @@ object Terminal { private[sbt] class WriteableInputStream(in: InputStream, name: String) extends SimpleInputStream with AutoCloseable { + private[this] val isRaw = new AtomicBoolean(false) final def write(bytes: Int*): Unit = readThread.synchronized { bytes.foreach(b => buffer.put(b)) } - def setRawMode(toggle: Boolean): Unit = in match { - case win: WindowsInputStream => win.setRawMode(toggle) - case _ => + def setRawMode(toggle: Boolean): Unit = { + isRaw.set(toggle) + in match { + case win: WindowsInputStream => win.setRawMode(toggle) + case _ => + } } private[this] val executor = Executors.newSingleThreadExecutor(r => new Thread(r, s"sbt-$name-input-reader")) @@ -479,8 +483,8 @@ object Terminal { val _ = readQueue.take val b = in.read buffer.put(b) - if (b != -1 && !Thread.interrupted()) impl() - else closed.set(true) + if (Thread.interrupted() || (b != -1 && !isRaw.get)) closed.set(true) + else impl() } try impl() catch { case _: InterruptedException => closed.set(true) }