mirror of https://github.com/sbt/sbt.git
Merge pull request #31 from sbt/wip/thread
Inject thread.sleep, which allows thread interruption during readLine
This commit is contained in:
commit
d84f82d8d4
|
|
@ -2,7 +2,7 @@ import Dependencies._
|
||||||
import Util._
|
import Util._
|
||||||
import com.typesafe.tools.mima.core._, ProblemFilters._
|
import com.typesafe.tools.mima.core._, ProblemFilters._
|
||||||
|
|
||||||
def baseVersion: String = "0.1.0-M8"
|
def baseVersion: String = "0.1.0-M9"
|
||||||
def internalPath = file("internal")
|
def internalPath = file("internal")
|
||||||
|
|
||||||
def commonSettings: Seq[Setting[_]] = Seq(
|
def commonSettings: Seq[Setting[_]] = Seq(
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ package sbt.internal.util
|
||||||
|
|
||||||
import jline.console.ConsoleReader
|
import jline.console.ConsoleReader
|
||||||
import jline.console.history.{ FileHistory, MemoryHistory }
|
import jline.console.history.{ FileHistory, MemoryHistory }
|
||||||
import java.io.{ File, InputStream, PrintWriter }
|
import java.io.{ File, InputStream, PrintWriter, FileInputStream, FileDescriptor, FilterInputStream }
|
||||||
import complete.Parser
|
import complete.Parser
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import scala.concurrent.duration.Duration
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
abstract class JLine extends LineReader {
|
abstract class JLine extends LineReader {
|
||||||
protected[this] val handleCONT: Boolean
|
protected[this] val handleCONT: Boolean
|
||||||
|
|
@ -15,13 +17,12 @@ abstract class JLine extends LineReader {
|
||||||
|
|
||||||
def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) }
|
def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) }
|
||||||
|
|
||||||
private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]) =
|
private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]): Option[String] =
|
||||||
readLineWithHistory(prompt, mask) match {
|
readLineWithHistory(prompt, mask) map { x =>
|
||||||
case null => None
|
x.trim
|
||||||
case x => Some(x.trim)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): String =
|
private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): Option[String] =
|
||||||
reader.getHistory match {
|
reader.getHistory match {
|
||||||
case fh: FileHistory =>
|
case fh: FileHistory =>
|
||||||
try { readLineDirect(prompt, mask) }
|
try { readLineDirect(prompt, mask) }
|
||||||
|
|
@ -29,17 +30,17 @@ abstract class JLine extends LineReader {
|
||||||
case _ => readLineDirect(prompt, mask)
|
case _ => readLineDirect(prompt, mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def readLineDirect(prompt: String, mask: Option[Char]): String =
|
private[this] def readLineDirect(prompt: String, mask: Option[Char]): Option[String] =
|
||||||
if (handleCONT)
|
if (handleCONT)
|
||||||
Signals.withHandler(() => resume(), signal = Signals.CONT)(() => readLineDirectRaw(prompt, mask))
|
Signals.withHandler(() => resume(), signal = Signals.CONT)(() => readLineDirectRaw(prompt, mask))
|
||||||
else
|
else
|
||||||
readLineDirectRaw(prompt, mask)
|
readLineDirectRaw(prompt, mask)
|
||||||
private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): String =
|
private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] =
|
||||||
{
|
{
|
||||||
val newprompt = handleMultilinePrompt(prompt)
|
val newprompt = handleMultilinePrompt(prompt)
|
||||||
mask match {
|
mask match {
|
||||||
case Some(m) => reader.readLine(newprompt, m)
|
case Some(m) => Option(reader.readLine(newprompt, m))
|
||||||
case None => reader.readLine(newprompt)
|
case None => Option(reader.readLine(newprompt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,10 +96,14 @@ private[sbt] object JLine {
|
||||||
t.restore
|
t.restore
|
||||||
f(t)
|
f(t)
|
||||||
}
|
}
|
||||||
def createReader(): ConsoleReader = createReader(None)
|
def createReader(): ConsoleReader = createReader(None, true)
|
||||||
def createReader(historyPath: Option[File]): ConsoleReader =
|
def createReader(historyPath: Option[File], injectThreadSleep: Boolean): ConsoleReader =
|
||||||
usingTerminal { t =>
|
usingTerminal { t =>
|
||||||
val cr = new ConsoleReader
|
val cr = if (injectThreadSleep) {
|
||||||
|
val originalIn = new FileInputStream(FileDescriptor.in)
|
||||||
|
val in = new InputStreamWrapper(originalIn, Duration("50 ms"))
|
||||||
|
new ConsoleReader(in, System.out)
|
||||||
|
} else new ConsoleReader
|
||||||
cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650
|
cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650
|
||||||
cr.setBellEnabled(false)
|
cr.setBellEnabled(false)
|
||||||
val h = historyPath match {
|
val h = historyPath match {
|
||||||
|
|
@ -116,25 +121,44 @@ private[sbt] object JLine {
|
||||||
finally { t.restore }
|
finally { t.restore }
|
||||||
}
|
}
|
||||||
|
|
||||||
def simple(historyPath: Option[File], handleCONT: Boolean = HandleCONT): SimpleReader = new SimpleReader(historyPath, handleCONT)
|
def simple(
|
||||||
|
historyPath: Option[File],
|
||||||
|
handleCONT: Boolean = HandleCONT,
|
||||||
|
injectThreadSleep: Boolean = true
|
||||||
|
): SimpleReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep)
|
||||||
val MaxHistorySize = 500
|
val MaxHistorySize = 500
|
||||||
val HandleCONT = !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT)
|
val HandleCONT = !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration) extends FilterInputStream(is) {
|
||||||
|
@tailrec
|
||||||
|
final override def read(): Int =
|
||||||
|
if (is.available() != 0) is.read()
|
||||||
|
else {
|
||||||
|
Thread.sleep(poll.toMillis)
|
||||||
|
read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trait LineReader {
|
trait LineReader {
|
||||||
def readLine(prompt: String, mask: Option[Char] = None): Option[String]
|
def readLine(prompt: String, mask: Option[Char] = None): Option[String]
|
||||||
}
|
}
|
||||||
final class FullReader(historyPath: Option[File], complete: Parser[_], val handleCONT: Boolean = JLine.HandleCONT) extends JLine {
|
final class FullReader(
|
||||||
|
historyPath: Option[File],
|
||||||
|
complete: Parser[_],
|
||||||
|
val handleCONT: Boolean = JLine.HandleCONT,
|
||||||
|
val injectThreadSleep: Boolean = true
|
||||||
|
) extends JLine {
|
||||||
protected[this] val reader =
|
protected[this] val reader =
|
||||||
{
|
{
|
||||||
val cr = JLine.createReader(historyPath)
|
val cr = JLine.createReader(historyPath, injectThreadSleep)
|
||||||
sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete)
|
sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete)
|
||||||
cr
|
cr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean) extends JLine {
|
class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean, val injectThreadSleep: Boolean) extends JLine {
|
||||||
protected[this] val reader = JLine.createReader(historyPath)
|
protected[this] val reader = JLine.createReader(historyPath, injectThreadSleep)
|
||||||
}
|
}
|
||||||
object SimpleReader extends SimpleReader(None, JLine.HandleCONT)
|
object SimpleReader extends SimpleReader(None, JLine.HandleCONT, true)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue