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.
This commit is contained in:
Ethan Atkins 2020-10-20 16:55:21 -07:00
parent 9f7985a2d9
commit 82d6a050de
1 changed files with 9 additions and 5 deletions

View File

@ -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) }