Support char buffered stdin on windows in continuous

I finally realized that the trick is that for non cygwin windows, the
available method on the jline wrapped input stream always returns zero.
Unlike on posix, however, the read method is interruptible which means
that we can just spin up a background thread that polls from the input
stream and writes it into a buffer.

I verified that it was no longer necessary to hit <enter> after 'r' to
rerun the continuous command on my windows vm after this change.
This commit is contained in:
Ethan Atkins 2019-05-11 19:20:29 -07:00
parent 4b915ff69e
commit b96be5343b
2 changed files with 42 additions and 11 deletions

View File

@ -9,7 +9,7 @@ package sbt
package internal
import java.io.{ ByteArrayInputStream, InputStream, File => _ }
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
import sbt.BasicCommandStrings.{
ContinuousExecutePrefix,
@ -269,13 +269,45 @@ private[sbt] object Continuous extends DeprecatedContinuous {
f(commands, s, valid, invalid)
}
private[this] def withCharBufferedStdIn[R](f: InputStream => R): R =
if (!Util.isWindows) {
val terminal = JLine.terminal
terminal.init()
terminal.setEchoEnabled(true)
f(terminal.wrapInIfNeeded(System.in))
} else f(System.in)
private[this] def withCharBufferedStdIn[R](f: InputStream => R): R = {
val terminal = JLine.terminal
terminal.init()
terminal.setEchoEnabled(true)
val wrapped = terminal.wrapInIfNeeded(System.in)
if (Util.isNonCygwinWindows) {
val inputStream: InputStream with AutoCloseable = new InputStream with AutoCloseable {
private[this] val buffer = new java.util.LinkedList[Int]
private[this] val closed = new AtomicBoolean(false)
private[this] val thread = new Thread("Continuous-input-stream-reader") {
setDaemon(true)
start()
@tailrec
override def run(): Unit = {
try {
if (!closed.get()) {
buffer.add(wrapped.read())
}
} catch {
case _: InterruptedException =>
}
if (!closed.get()) run()
}
}
override def available(): Int = buffer.size()
override def read(): Int = buffer.poll()
override def close(): Unit = if (closed.compareAndSet(false, true)) {
thread.interrupt()
}
}
try {
f(inputStream)
} finally {
inputStream.close()
}
} else {
f(wrapped)
}
}
private[sbt] def runToTermination(
state: State,

View File

@ -393,11 +393,10 @@ object Watch {
private[this] val options = {
val enter = "<enter>"
val newLine = if (Util.isWindows) enter else ""
val opts = Seq(
s"$enter: return to the shell",
s"'r$newLine': repeat the current command",
s"'x$newLine': exit sbt"
s"'r': repeat the current command",
s"'x': exit sbt"
)
s"Options:\n${opts.mkString(" ", "\n ", "")}"
}