mirror of https://github.com/sbt/sbt.git
Merge pull request #5874 from eatkins/terminal-fixes
Fix a number of terminal io bugs
This commit is contained in:
commit
23fd24c838
|
|
@ -368,6 +368,7 @@ lazy val utilLogging = (project in file("internal") / "util-logging")
|
|||
Seq(
|
||||
jline,
|
||||
jline3Terminal,
|
||||
jline3JNA,
|
||||
jline3Jansi,
|
||||
log4jApi,
|
||||
log4jCore,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import org.jline.reader.{
|
|||
ParsedLine,
|
||||
UserInterruptException,
|
||||
}
|
||||
import org.jline.utils.ClosedException
|
||||
import sbt.internal.util.complete.Parser
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
|
@ -87,7 +88,7 @@ object LineReader {
|
|||
case e: EndOfFileException =>
|
||||
if (terminal == Terminal.console && System.console == null) None
|
||||
else Some("exit")
|
||||
case _: IOError => Some("exit")
|
||||
case _: IOError | _: ClosedException => Some("exit")
|
||||
case _: UserInterruptException | _: ClosedByInterruptException |
|
||||
_: UncheckedIOException =>
|
||||
throw new InterruptedException
|
||||
|
|
@ -221,8 +222,7 @@ private[sbt] object JLine {
|
|||
"Don't use jline.Terminal directly. Use Terminal.get.withCanonicalIn instead.",
|
||||
"1.4.0"
|
||||
)
|
||||
def usingTerminal[T](f: jline.Terminal => T): T =
|
||||
Terminal.get.withCanonicalIn(f(Terminal.get.toJLine))
|
||||
def usingTerminal[T](f: jline.Terminal => T): T = f(Terminal.get.toJLine)
|
||||
|
||||
@deprecated("unused", "1.4.0")
|
||||
def createReader(): ConsoleReader = createReader(None, Terminal.wrappedSystemIn)
|
||||
|
|
|
|||
|
|
@ -12,67 +12,64 @@ import java.nio.charset.Charset
|
|||
import java.util.{ Arrays, EnumSet }
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
||||
import org.jline.utils.InfoCmp.Capability
|
||||
import org.jline.utils.{ ClosedException, NonBlockingReader, OSUtils }
|
||||
import org.jline.utils.{ ClosedException, NonBlockingReader }
|
||||
import org.jline.terminal.{ Attributes, Size, Terminal => JTerminal }
|
||||
import org.jline.terminal.Attributes.{ InputFlag, LocalFlag }
|
||||
import org.jline.terminal.Terminal.SignalHandler
|
||||
import org.jline.terminal.impl.{ AbstractTerminal, DumbTerminal }
|
||||
import org.jline.terminal.impl.jansi.JansiSupportImpl
|
||||
import org.jline.terminal.impl.jansi.win.JansiWinSysTerminal
|
||||
import org.jline.utils.OSUtils
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.util.Try
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
|
||||
private[sbt] object JLine3 {
|
||||
private val capabilityMap = Capability
|
||||
.values()
|
||||
.map { c =>
|
||||
c.toString -> c
|
||||
}
|
||||
.toMap
|
||||
private[util] val initialAttributes = new AtomicReference[Attributes]
|
||||
|
||||
private[util] def system = {
|
||||
/*
|
||||
* For reasons that are unclear to me, TerminalBuilder fails to build
|
||||
* windows terminals. The instructions about the classpath did not work:
|
||||
* https://stackoverflow.com/questions/52851232/jline3-issues-with-windows-terminal
|
||||
* We can deconstruct what TerminalBuilder does and inline it for now.
|
||||
* It is possible that this workaround will break WSL but I haven't checked that.
|
||||
*/
|
||||
if (Util.isNonCygwinWindows) {
|
||||
val support = new JansiSupportImpl
|
||||
val winConsole = support.isWindowsConsole();
|
||||
try {
|
||||
val term = JansiWinSysTerminal.createTerminal(
|
||||
"console",
|
||||
"ansi",
|
||||
OSUtils.IS_CONEMU,
|
||||
Charset.forName("UTF-8"),
|
||||
-1,
|
||||
false,
|
||||
SignalHandler.SIG_DFL,
|
||||
true
|
||||
)
|
||||
term.disableScrolling()
|
||||
term
|
||||
} catch {
|
||||
case _: Exception =>
|
||||
org.jline.terminal.TerminalBuilder
|
||||
.builder()
|
||||
.system(false)
|
||||
.paused(true)
|
||||
.jansi(true)
|
||||
.streams(Terminal.console.inputStream, Terminal.console.outputStream)
|
||||
.build()
|
||||
private[this] val forceWindowsJansiHolder = new AtomicBoolean(false)
|
||||
private[sbt] def forceWindowsJansi(): Unit = forceWindowsJansiHolder.set(true)
|
||||
private[this] def windowsJansi(): org.jline.terminal.Terminal = {
|
||||
val support = new JansiSupportImpl
|
||||
val winConsole = support.isWindowsConsole();
|
||||
val termType = sys.props.get("org.jline.terminal.type").orElse(sys.env.get("TERM")).orNull
|
||||
val term = JansiWinSysTerminal.createTerminal(
|
||||
"console",
|
||||
termType,
|
||||
OSUtils.IS_CONEMU,
|
||||
Charset.forName("UTF-8"),
|
||||
-1,
|
||||
false,
|
||||
SignalHandler.SIG_DFL,
|
||||
true
|
||||
)
|
||||
term.disableScrolling()
|
||||
term
|
||||
}
|
||||
private val jansi = {
|
||||
val (major, minor) =
|
||||
(JansiSupportImpl.getJansiMajorVersion, JansiSupportImpl.getJansiMinorVersion)
|
||||
(major > 1 || minor >= 18) && Util.isWindows
|
||||
}
|
||||
private[util] def system: org.jline.terminal.Terminal = {
|
||||
val term =
|
||||
if (forceWindowsJansiHolder.get) windowsJansi()
|
||||
else {
|
||||
// Only use jna on windows. Both jna and jansi use illegal reflective
|
||||
// accesses on posix system.
|
||||
org.jline.terminal.TerminalBuilder
|
||||
.builder()
|
||||
.system(System.console != null)
|
||||
.jna(Util.isWindows && !jansi)
|
||||
.jansi(jansi)
|
||||
.paused(true)
|
||||
.build()
|
||||
}
|
||||
} else {
|
||||
org.jline.terminal.TerminalBuilder
|
||||
.builder()
|
||||
.system(System.console != null)
|
||||
.paused(true)
|
||||
.jna(false)
|
||||
.jansi(true)
|
||||
.build()
|
||||
initialAttributes.get match {
|
||||
case null => initialAttributes.set(term.getAttributes)
|
||||
case _ =>
|
||||
}
|
||||
term
|
||||
}
|
||||
private[sbt] def apply(term: Terminal): JTerminal = {
|
||||
if (System.getProperty("jline.terminal", "") == "none" || !Terminal.formatEnabledInEnv)
|
||||
|
|
@ -80,7 +77,12 @@ private[sbt] object JLine3 {
|
|||
else wrapTerminal(term)
|
||||
}
|
||||
private[this] def wrapTerminal(term: Terminal): JTerminal = {
|
||||
new AbstractTerminal(term.name, "ansi", Charset.forName("UTF-8"), SignalHandler.SIG_DFL) {
|
||||
new AbstractTerminal(
|
||||
term.name,
|
||||
"nocapabilities",
|
||||
Charset.forName("UTF-8"),
|
||||
SignalHandler.SIG_DFL
|
||||
) {
|
||||
val closed = new AtomicBoolean(false)
|
||||
setOnClose { () =>
|
||||
doClose()
|
||||
|
|
@ -94,7 +96,6 @@ private[sbt] object JLine3 {
|
|||
}
|
||||
}
|
||||
}
|
||||
parseInfoCmp()
|
||||
override val input: InputStream = new InputStream {
|
||||
override def read: Int = {
|
||||
val res = term.inputStream match {
|
||||
|
|
@ -176,45 +177,52 @@ private[sbt] object JLine3 {
|
|||
* are the same.
|
||||
*/
|
||||
override def getStringCapability(cap: Capability): String = {
|
||||
term.getStringCapability(cap.toString, jline3 = true)
|
||||
}
|
||||
override def getNumericCapability(cap: Capability): Integer = {
|
||||
term.getNumericCapability(cap.toString, jline3 = true)
|
||||
}
|
||||
override def getBooleanCapability(cap: Capability): Boolean = {
|
||||
term.getBooleanCapability(cap.toString, jline3 = true)
|
||||
term.getStringCapability(cap.toString) match {
|
||||
case null if cap == Capability.key_dc && Util.isWindows => "\\E[3~"
|
||||
case null if cap == Capability.key_end && Util.isWindows => "\\E[4~"
|
||||
case null if cap == Capability.key_ic && Util.isWindows => "\\E[2~"
|
||||
case c => c
|
||||
}
|
||||
}
|
||||
override def getNumericCapability(cap: Capability): Integer =
|
||||
term.getNumericCapability(cap.toString)
|
||||
override def getBooleanCapability(cap: Capability): Boolean =
|
||||
term.getBooleanCapability(cap.toString)
|
||||
def getAttributes(): Attributes = attributesFromMap(term.getAttributes)
|
||||
def getSize(): Size = new Size(term.getWidth, term.getHeight)
|
||||
def setAttributes(a: Attributes): Unit = term.setAttributes(toMap(a))
|
||||
def setAttributes(a: Attributes): Unit = {} // don't allow the jline line reader to change attributes
|
||||
def setSize(size: Size): Unit = term.setSize(size.getColumns, size.getRows)
|
||||
|
||||
/**
|
||||
* Override enterRawMode because the default implementation modifies System.in
|
||||
* to be non-blocking which means it immediately returns -1 if there is no
|
||||
* data available, which is not desirable for us.
|
||||
*/
|
||||
override def enterRawMode(): Attributes = enterRawModeImpl(this)
|
||||
override def enterRawMode(): Attributes = {
|
||||
// don't actually modify the term, that is handled by LineReader
|
||||
attributesFromMap(term.getAttributes)
|
||||
}
|
||||
}
|
||||
}
|
||||
private def enterRawModeImpl(term: JTerminal): Attributes = {
|
||||
val prvAttr = term.getAttributes()
|
||||
val newAttr = new Attributes(prvAttr)
|
||||
newAttr.setLocalFlags(
|
||||
EnumSet
|
||||
.of(Attributes.LocalFlag.ICANON, Attributes.LocalFlag.ECHO, Attributes.LocalFlag.IEXTEN),
|
||||
false
|
||||
)
|
||||
newAttr.setInputFlags(
|
||||
EnumSet
|
||||
.of(Attributes.InputFlag.IXON, Attributes.InputFlag.ICRNL, Attributes.InputFlag.INLCR),
|
||||
false
|
||||
)
|
||||
newAttr.setLocalFlags(EnumSet.of(LocalFlag.ICANON, LocalFlag.ECHO, LocalFlag.IEXTEN), false)
|
||||
newAttr.setInputFlags(EnumSet.of(InputFlag.IXON, InputFlag.ICRNL, InputFlag.INLCR), false)
|
||||
term.setAttributes(newAttr)
|
||||
prvAttr
|
||||
}
|
||||
private[util] def enterRawMode(term: JTerminal): Map[String, String] =
|
||||
toMap(enterRawModeImpl(term))
|
||||
private[util] def enterRawMode(term: JTerminal): Unit = {
|
||||
val prevAttr = initialAttributes.get
|
||||
val newAttr = new Attributes(prevAttr)
|
||||
// These flags are copied from the jline3 enterRawMode but the jline implementation
|
||||
// also puts the input stream in non blocking mode, which we do not want.
|
||||
newAttr.setLocalFlags(EnumSet.of(LocalFlag.ICANON, LocalFlag.IEXTEN, LocalFlag.ECHO), false)
|
||||
newAttr.setInputFlags(EnumSet.of(InputFlag.IXON, InputFlag.ICRNL, InputFlag.INLCR), false)
|
||||
term.setAttributes(newAttr)
|
||||
()
|
||||
}
|
||||
private[util] def exitRawMode(term: JTerminal): Unit = {
|
||||
val initAttr = initialAttributes.get
|
||||
val newAttr = new Attributes(initAttr)
|
||||
newAttr.setLocalFlags(EnumSet.of(LocalFlag.ICANON, LocalFlag.ECHO), true)
|
||||
term.setAttributes(newAttr)
|
||||
}
|
||||
private[util] def toMap(jattributes: Attributes): Map[String, String] = {
|
||||
val result = new java.util.LinkedHashMap[String, String]
|
||||
result.put(
|
||||
|
|
@ -243,14 +251,14 @@ private[sbt] object JLine3 {
|
|||
)
|
||||
result.asScala.toMap
|
||||
}
|
||||
private[this] val iflagMap: Map[String, Attributes.InputFlag] =
|
||||
Attributes.InputFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val iflagMap: Map[String, InputFlag] =
|
||||
InputFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val oflagMap: Map[String, Attributes.OutputFlag] =
|
||||
Attributes.OutputFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val cflagMap: Map[String, Attributes.ControlFlag] =
|
||||
Attributes.ControlFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val lflagMap: Map[String, Attributes.LocalFlag] =
|
||||
Attributes.LocalFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val lflagMap: Map[String, LocalFlag] =
|
||||
LocalFlag.values.map(f => f.name.toLowerCase -> f).toMap
|
||||
private[this] val charMap: Map[String, Attributes.ControlChar] =
|
||||
Attributes.ControlChar.values().map(f => f.name.toLowerCase -> f).toMap
|
||||
private[util] def attributesFromMap(map: Map[String, String]): Attributes = {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import java.util.{ Arrays, Locale }
|
|||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
||||
import java.util.concurrent.{ Executors, LinkedBlockingQueue, TimeUnit }
|
||||
|
||||
import jline.DefaultTerminal2
|
||||
import jline.console.ConsoleReader
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -104,6 +103,13 @@ trait Terminal extends AutoCloseable {
|
|||
*/
|
||||
def isSupershellEnabled: Boolean
|
||||
|
||||
/**
|
||||
* Toggles whether or not the terminal should echo characters back to stdout
|
||||
*
|
||||
* @return the previous value of the toggle
|
||||
*/
|
||||
def setEchoEnabled(toggle: Boolean): Unit
|
||||
|
||||
/*
|
||||
* The methods below this comment are implementation details that are in
|
||||
* some cases specific to jline2. These methods may need to change or be
|
||||
|
|
@ -126,16 +132,21 @@ trait Terminal extends AutoCloseable {
|
|||
*/
|
||||
private[sbt] def getLines: Seq[String]
|
||||
|
||||
private[sbt] def getBooleanCapability(capability: String, jline3: Boolean): Boolean
|
||||
private[sbt] def getNumericCapability(capability: String, jline3: Boolean): Integer
|
||||
private[sbt] def getStringCapability(capability: String, jline3: Boolean): String
|
||||
private[sbt] def getBooleanCapability(capability: String): Boolean
|
||||
private[sbt] def getNumericCapability(capability: String): Integer
|
||||
private[sbt] def getStringCapability(capability: String): String
|
||||
private[sbt] def getAttributes: Map[String, String]
|
||||
private[sbt] def setAttributes(attributes: Map[String, String]): Unit
|
||||
private[sbt] def setSize(width: Int, height: Int): Unit
|
||||
|
||||
private[sbt] def name: String
|
||||
private[sbt] def withRawInput[T](f: => T): T = f
|
||||
private[sbt] def withCanonicalIn[T](f: => T): T = f
|
||||
private[sbt] final def withRawInput[T](f: => T): T = {
|
||||
enterRawMode()
|
||||
try f
|
||||
catch { case e: InterruptedIOException => throw new InterruptedException } finally exitRawMode()
|
||||
}
|
||||
private[sbt] def enterRawMode(): Unit
|
||||
private[sbt] def exitRawMode(): Unit
|
||||
private[sbt] def write(bytes: Int*): Unit
|
||||
private[sbt] def printStream: PrintStream
|
||||
private[sbt] def withPrintStream[T](f: PrintStream => T): T
|
||||
|
|
@ -189,7 +200,6 @@ object Terminal {
|
|||
* terminal.
|
||||
*/
|
||||
private[sbt] def toJLine: jline.Terminal with jline.Terminal2 = term match {
|
||||
case t: ConsoleTerminal => t.term
|
||||
case _ =>
|
||||
new jline.Terminal with jline.Terminal2 {
|
||||
override def init(): Unit = {}
|
||||
|
|
@ -207,15 +217,12 @@ object Terminal {
|
|||
override def disableInterruptCharacter(): Unit = {}
|
||||
override def enableInterruptCharacter(): Unit = {}
|
||||
override def getOutputEncoding: String = null
|
||||
override def getBooleanCapability(capability: String): Boolean = {
|
||||
term.getBooleanCapability(capability, jline3 = false)
|
||||
}
|
||||
override def getNumericCapability(capability: String): Integer = {
|
||||
term.getNumericCapability(capability, jline3 = false)
|
||||
}
|
||||
override def getStringCapability(capability: String): String = {
|
||||
term.getStringCapability(capability, jline3 = false)
|
||||
}
|
||||
override def getBooleanCapability(capability: String): Boolean =
|
||||
term.getBooleanCapability(capability)
|
||||
override def getNumericCapability(capability: String): Integer =
|
||||
term.getNumericCapability(capability)
|
||||
override def getStringCapability(capability: String): String =
|
||||
term.getStringCapability(capability)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -319,7 +326,7 @@ object Terminal {
|
|||
// In ci environments, don't touch the io streams unless run with -Dsbt.io.virtual=true
|
||||
if (System.getProperty("sbt.io.virtual", "") == "true" || (logFormatEnabled.getOrElse(true) && !isCI)) {
|
||||
hasProgress.set(isServer)
|
||||
consoleTerminalHolder.set(wrap(jline.TerminalFactory.get))
|
||||
consoleTerminalHolder.set(newConsoleTerminal())
|
||||
activeTerminal.set(consoleTerminalHolder.get)
|
||||
try withOut(withIn(f))
|
||||
finally {
|
||||
|
|
@ -334,7 +341,7 @@ object Terminal {
|
|||
* back to blocking mode. We can then close the console. We do
|
||||
* this on a background thread in case the read blocks indefinitely.
|
||||
*/
|
||||
val prev = c.system.enterRawMode()
|
||||
c.system.enterRawMode()
|
||||
val runnable: Runnable = () => {
|
||||
try Util.ignoreResult(c.inputStream.read)
|
||||
catch { case _: InterruptedException => }
|
||||
|
|
@ -345,7 +352,6 @@ object Terminal {
|
|||
// The thread should exit almost instantly but give it 200ms to spin up
|
||||
thread.join(200)
|
||||
if (thread.isAlive) thread.interrupt()
|
||||
c.system.setAttributes(prev)
|
||||
c.close()
|
||||
case c => c.close()
|
||||
}
|
||||
|
|
@ -358,6 +364,8 @@ object Terminal {
|
|||
private[this] object ProxyTerminal extends Terminal {
|
||||
private def t: Terminal = activeTerminal.get
|
||||
override private[sbt] def progressState: ProgressState = t.progressState
|
||||
override private[sbt] def enterRawMode(): Unit = t.enterRawMode()
|
||||
override private[sbt] def exitRawMode(): Unit = t.exitRawMode()
|
||||
override def getWidth: Int = t.getWidth
|
||||
override def getHeight: Int = t.getHeight
|
||||
override def getLineHeightAndWidth(line: String): (Int, Int) = t.getLineHeightAndWidth(line)
|
||||
|
|
@ -370,18 +378,17 @@ object Terminal {
|
|||
override def isEchoEnabled: Boolean = t.isEchoEnabled
|
||||
override def isSuccessEnabled: Boolean = t.isSuccessEnabled
|
||||
override def isSupershellEnabled: Boolean = t.isSupershellEnabled
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean =
|
||||
t.getBooleanCapability(capability, jline3)
|
||||
override def getNumericCapability(capability: String, jline3: Boolean): Integer =
|
||||
t.getNumericCapability(capability, jline3)
|
||||
override def getStringCapability(capability: String, jline3: Boolean): String =
|
||||
t.getStringCapability(capability, jline3)
|
||||
override def setEchoEnabled(toggle: Boolean): Unit = t.setEchoEnabled(toggle)
|
||||
override def getBooleanCapability(capability: String): Boolean =
|
||||
t.getBooleanCapability(capability)
|
||||
override def getNumericCapability(capability: String): Integer =
|
||||
t.getNumericCapability(capability)
|
||||
override def getStringCapability(capability: String): String =
|
||||
t.getStringCapability(capability)
|
||||
override private[sbt] def getAttributes: Map[String, String] = t.getAttributes
|
||||
override private[sbt] def setAttributes(attributes: Map[String, String]): Unit =
|
||||
t.setAttributes(attributes)
|
||||
override private[sbt] def setSize(width: Int, height: Int): Unit = t.setSize(width, height)
|
||||
override def withRawInput[T](f: => T): T = t.withRawInput(f)
|
||||
override def withCanonicalIn[T](f: => T): T = t.withCanonicalIn(f)
|
||||
override def printStream: PrintStream = t.printStream
|
||||
override def withPrintStream[T](f: PrintStream => T): T = t.withPrintStream(f)
|
||||
override private[sbt] def withRawOutput[R](f: => R): R = t.withRawOutput(f)
|
||||
|
|
@ -435,11 +442,15 @@ object Terminal {
|
|||
private[this] val originalErr = System.err
|
||||
private[this] val originalIn = System.in
|
||||
private[sbt] class WriteableInputStream(in: InputStream, name: String)
|
||||
extends InputStream
|
||||
extends SimpleInputStream
|
||||
with AutoCloseable {
|
||||
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 _ =>
|
||||
}
|
||||
private[this] val executor =
|
||||
Executors.newSingleThreadExecutor(r => new Thread(r, s"sbt-$name-input-reader"))
|
||||
private[this] val buffer = new LinkedBlockingQueue[Integer]
|
||||
|
|
@ -502,8 +513,10 @@ object Terminal {
|
|||
()
|
||||
}
|
||||
}
|
||||
private[this] lazy val nonBlockingIn: WriteableInputStream =
|
||||
new WriteableInputStream(jline.TerminalFactory.get.wrapInIfNeeded(originalIn), "console")
|
||||
private[this] def nonBlockingIn(term: org.jline.terminal.Terminal): WriteableInputStream = {
|
||||
val in = if (Util.isNonCygwinWindows) new WindowsInputStream(term, originalIn) else originalIn
|
||||
new WriteableInputStream(in, "console")
|
||||
}
|
||||
|
||||
private[this] val inputStream = new AtomicReference[InputStream](System.in)
|
||||
private[this] def withOut[T](f: => T): T = {
|
||||
|
|
@ -518,8 +531,8 @@ object Terminal {
|
|||
}
|
||||
private[this] def withIn[T](f: => T): T =
|
||||
try {
|
||||
inputStream.set(Terminal.wrappedSystemIn)
|
||||
System.setIn(wrappedSystemIn)
|
||||
inputStream.set(proxyInputStream)
|
||||
System.setIn(proxyInputStream)
|
||||
scala.Console.withIn(proxyInputStream)(f)
|
||||
} finally System.setIn(originalIn)
|
||||
|
||||
|
|
@ -567,7 +580,15 @@ object Terminal {
|
|||
bootOutputStreamHolder.set(bootOutputStream)
|
||||
}
|
||||
|
||||
private[this] object proxyInputStream extends InputStream {
|
||||
private[sbt] trait SimpleInputStream extends InputStream {
|
||||
override def read(b: Array[Byte]): Int = read(b, 0, b.length)
|
||||
override def read(b: Array[Byte], off: Int, len: Int): Int = {
|
||||
val byte = read()
|
||||
b(off) = byte.toByte
|
||||
1
|
||||
}
|
||||
}
|
||||
private[this] object proxyInputStream extends SimpleInputStream {
|
||||
private[this] val isScripted = System.getProperty("sbt.scripted", "false") == "true"
|
||||
/*
|
||||
* This is to handle the case when a remote client starts sbt and the build fails.
|
||||
|
|
@ -645,10 +666,10 @@ object Terminal {
|
|||
os.write(bytes, offset, len)
|
||||
override def flush(): Unit = os.flush()
|
||||
}
|
||||
private[this] val proxyErrorStream = new PrintStream(proxyErrorOutputStream, true)
|
||||
private[this] object proxyErrorStream extends PrintStream(proxyErrorOutputStream, true)
|
||||
private[this] lazy val isWindows =
|
||||
System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).indexOf("windows") >= 0
|
||||
private[this] object WrappedSystemIn extends InputStream {
|
||||
private[this] object WrappedSystemIn extends SimpleInputStream {
|
||||
private[this] val in = proxyInputStream
|
||||
override def available(): Int = if (attached.get) in.available() else 0
|
||||
override def read(): Int = synchronized {
|
||||
|
|
@ -693,75 +714,16 @@ object Terminal {
|
|||
}
|
||||
private[sbt] def startedByRemoteClient = props.isDefined
|
||||
|
||||
/**
|
||||
* Creates an instance of [[Terminal]] that delegates most of its methods to an underlying
|
||||
* jline.Terminal2 instance. In the long run, sbt should upgrade to jline3, which has a
|
||||
* completely different terminal interface so whereever possible, we should avoid
|
||||
* directly referencing jline.Terminal. Wrapping jline Terminal in sbt terminal helps
|
||||
* with that goal.
|
||||
*
|
||||
* @param terminal the jline terminal to wrap
|
||||
* @return an sbt Terminal
|
||||
*/
|
||||
private[this] def wrap(terminal: jline.Terminal): Terminal = {
|
||||
val term: jline.Terminal with jline.Terminal2 = new jline.Terminal with jline.Terminal2 {
|
||||
private[this] val hasConsole = System.console != null
|
||||
private[this] def alive = hasConsole && attached.get
|
||||
private[this] val term2: jline.Terminal2 = terminal match {
|
||||
case t: jline.Terminal2 => t
|
||||
case _ => new DefaultTerminal2(terminal)
|
||||
}
|
||||
override def init(): Unit =
|
||||
if (alive)
|
||||
try terminal.init()
|
||||
catch {
|
||||
case _: InterruptedException | _: java.io.IOError =>
|
||||
}
|
||||
override def restore(): Unit =
|
||||
try terminal.restore()
|
||||
catch {
|
||||
case _: InterruptedException | _: java.io.IOError =>
|
||||
}
|
||||
override def reset(): Unit =
|
||||
try terminal.reset()
|
||||
catch { case _: InterruptedException => }
|
||||
override def isSupported: Boolean = terminal.isSupported
|
||||
override def getWidth: Int = props.map(_.width).getOrElse(terminal.getWidth)
|
||||
override def getHeight: Int = props.map(_.height).getOrElse(terminal.getHeight)
|
||||
override val isAnsiSupported: Boolean = terminal.isAnsiSupported && formatEnabledInEnv
|
||||
override def wrapOutIfNeeded(out: OutputStream): OutputStream = terminal.wrapOutIfNeeded(out)
|
||||
override def wrapInIfNeeded(in: InputStream): InputStream = terminal.wrapInIfNeeded(in)
|
||||
override def hasWeirdWrap: Boolean = terminal.hasWeirdWrap
|
||||
override def isEchoEnabled: Boolean = terminal.isEchoEnabled
|
||||
|
||||
override def setEchoEnabled(enabled: Boolean): Unit =
|
||||
if (alive) terminal.setEchoEnabled(enabled)
|
||||
override def disableInterruptCharacter(): Unit =
|
||||
if (alive) terminal.disableInterruptCharacter()
|
||||
override def enableInterruptCharacter(): Unit =
|
||||
if (alive) terminal.enableInterruptCharacter()
|
||||
override def getOutputEncoding: String = terminal.getOutputEncoding
|
||||
override def getBooleanCapability(capability: String): Boolean =
|
||||
term2.getBooleanCapability(capability)
|
||||
override def getNumericCapability(capability: String): Integer =
|
||||
term2.getNumericCapability(capability)
|
||||
override def getStringCapability(capability: String): String = {
|
||||
term2.getStringCapability(capability)
|
||||
}
|
||||
}
|
||||
term.restore()
|
||||
term.setEchoEnabled(true)
|
||||
new ConsoleTerminal(
|
||||
term,
|
||||
if (System.console == null) nullWriteableInputStream else nonBlockingIn,
|
||||
originalOut
|
||||
)
|
||||
private[this] def newConsoleTerminal(): Terminal = {
|
||||
val system = JLine3.system
|
||||
val in = if (System.console == null) nullWriteableInputStream else nonBlockingIn(system)
|
||||
new ConsoleTerminal(in, originalOut, system)
|
||||
}
|
||||
|
||||
private[sbt] def reset(): Unit = {
|
||||
jline.TerminalFactory.reset()
|
||||
console.close()
|
||||
consoleTerminalHolder.set(wrap(jline.TerminalFactory.get))
|
||||
consoleTerminalHolder.set(newConsoleTerminal())
|
||||
}
|
||||
|
||||
// translate explicit class names to type in order to support
|
||||
|
|
@ -807,52 +769,58 @@ object Terminal {
|
|||
|
||||
@deprecated("For compatibility only", "1.4.0")
|
||||
private[sbt] def deprecatedTeminal: jline.Terminal = console.toJLine
|
||||
private class ConsoleTerminal(
|
||||
val term: jline.Terminal with jline.Terminal2,
|
||||
private[util] class ConsoleTerminal(
|
||||
in: WriteableInputStream,
|
||||
out: OutputStream
|
||||
out: OutputStream,
|
||||
private[util] val system: org.jline.terminal.Terminal,
|
||||
) extends TerminalImpl(in, out, originalErr, "console0") {
|
||||
private[util] lazy val system = JLine3.system
|
||||
private[this] val rawMode = new AtomicBoolean(false)
|
||||
enterRawMode()
|
||||
override private[sbt] def getSizeImpl: (Int, Int) = {
|
||||
val size = system.getSize
|
||||
(size.getColumns, size.getRows)
|
||||
}
|
||||
override lazy val isAnsiSupported: Boolean = term.isAnsiSupported && !isCI
|
||||
override lazy val isAnsiSupported: Boolean = formatEnabledInEnv && !isCI
|
||||
override private[sbt] def progressState: ProgressState = consoleProgressState.get
|
||||
override def isEchoEnabled: Boolean = system.echo()
|
||||
override def isEchoEnabled: Boolean =
|
||||
try system.echo()
|
||||
catch { case _: InterruptedIOException => false }
|
||||
override def isSuccessEnabled: Boolean = true
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean =
|
||||
if (jline3) capabilityMap.get(capability).fold(false)(system.getBooleanCapability)
|
||||
else term.getBooleanCapability(capability)
|
||||
override def getNumericCapability(capability: String, jline3: Boolean): Integer =
|
||||
if (jline3) capabilityMap.get(capability).fold(null: Integer)(system.getNumericCapability)
|
||||
else term.getNumericCapability(capability)
|
||||
override def getStringCapability(capability: String, jline3: Boolean): String =
|
||||
if (jline3) capabilityMap.get(capability).fold(null: String)(system.getStringCapability)
|
||||
else term.getStringCapability(capability)
|
||||
override private[sbt] def restore(): Unit = term.restore()
|
||||
override def setEchoEnabled(toggle: Boolean): Unit =
|
||||
try Util.ignoreResult(system.echo(toggle))
|
||||
catch { case _: InterruptedIOException => }
|
||||
override def getBooleanCapability(capability: String): Boolean =
|
||||
capabilityMap.get(capability).fold(false)(system.getBooleanCapability)
|
||||
override def getNumericCapability(capability: String): Integer =
|
||||
capabilityMap.get(capability).fold(null: Integer)(system.getNumericCapability)
|
||||
override def getStringCapability(capability: String): String = {
|
||||
val res = capabilityMap.get(capability).fold(null: String)(system.getStringCapability)
|
||||
res
|
||||
}
|
||||
override private[sbt] def restore(): Unit = exitRawMode()
|
||||
|
||||
override private[sbt] def getAttributes: Map[String, String] =
|
||||
Try(JLine3.toMap(system.getAttributes)).getOrElse(Map.empty)
|
||||
override private[sbt] def setAttributes(attributes: Map[String, String]): Unit =
|
||||
override private[sbt] def setAttributes(attributes: Map[String, String]): Unit = {
|
||||
system.setAttributes(JLine3.attributesFromMap(attributes))
|
||||
}
|
||||
override private[sbt] def setSize(width: Int, height: Int): Unit =
|
||||
system.setSize(new org.jline.terminal.Size(width, height))
|
||||
|
||||
override def withRawInput[T](f: => T): T = term.synchronized {
|
||||
try {
|
||||
term.init()
|
||||
term.setEchoEnabled(false)
|
||||
f
|
||||
} catch { case _: InterruptedIOException => throw new InterruptedException } finally {
|
||||
term.restore()
|
||||
term.setEchoEnabled(true)
|
||||
}
|
||||
override private[sbt] def enterRawMode(): Unit = if (rawMode.compareAndSet(false, true)) {
|
||||
in.setRawMode(true)
|
||||
try JLine3.enterRawMode(system)
|
||||
catch { case _: java.io.IOError => }
|
||||
}
|
||||
override private[sbt] def exitRawMode(): Unit = if (rawMode.compareAndSet(true, false)) {
|
||||
in.setRawMode(false)
|
||||
try JLine3.exitRawMode(system)
|
||||
catch { case _: java.io.IOError => }
|
||||
}
|
||||
override def isColorEnabled: Boolean =
|
||||
props
|
||||
.map(_.color)
|
||||
.getOrElse(isColorEnabledProp.getOrElse(term.isAnsiSupported && formatEnabledInEnv))
|
||||
.getOrElse(isColorEnabledProp.getOrElse(formatEnabledInEnv))
|
||||
|
||||
override def isSupershellEnabled: Boolean =
|
||||
props
|
||||
|
|
@ -865,8 +833,9 @@ object Terminal {
|
|||
})
|
||||
override def close(): Unit = {
|
||||
try {
|
||||
system.setAttributes(JLine3.initialAttributes.get)
|
||||
system.close()
|
||||
term.restore()
|
||||
in.close()
|
||||
} catch { case NonFatal(_) => }
|
||||
super.close()
|
||||
}
|
||||
|
|
@ -966,13 +935,15 @@ object Terminal {
|
|||
private[sbt] class DefaultTerminal extends Terminal {
|
||||
override def close(): Unit = {}
|
||||
override private[sbt] def progressState: ProgressState = new ProgressState(1)
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean = false
|
||||
override private[sbt] def enterRawMode(): Unit = {}
|
||||
override private[sbt] def exitRawMode(): Unit = {}
|
||||
override def getBooleanCapability(capability: String): Boolean = false
|
||||
override def getHeight: Int = 0
|
||||
override def getLastLine: Option[String] = None
|
||||
override def getLines: Seq[String] = Nil
|
||||
override def getLineHeightAndWidth(line: String): (Int, Int) = (0, 0)
|
||||
override def getNumericCapability(capability: String, jline3: Boolean): Integer = null
|
||||
override def getStringCapability(capability: String, jline3: Boolean): String = null
|
||||
override def getNumericCapability(capability: String): Integer = null
|
||||
override def getStringCapability(capability: String): String = null
|
||||
override def getWidth: Int = 0
|
||||
override def inputStream: InputStream = nullInputStream
|
||||
override def isAnsiSupported: Boolean = false
|
||||
|
|
@ -980,6 +951,7 @@ object Terminal {
|
|||
override def isEchoEnabled: Boolean = false
|
||||
override def isSuccessEnabled: Boolean = true
|
||||
override def isSupershellEnabled: Boolean = false
|
||||
override def setEchoEnabled(toggle: Boolean): Unit = {}
|
||||
override def outputStream: OutputStream = _ => {}
|
||||
override def errorStream: OutputStream = _ => {}
|
||||
override private[sbt] def getAttributes: Map[String, String] = Map.empty
|
||||
|
|
@ -994,7 +966,7 @@ object Terminal {
|
|||
}
|
||||
private[sbt] object NullTerminal extends DefaultTerminal
|
||||
private[sbt] object SimpleTerminal extends DefaultTerminal {
|
||||
override lazy val inputStream: InputStream = nonBlockingIn
|
||||
override lazy val inputStream: InputStream = originalIn
|
||||
override lazy val outputStream: OutputStream = originalOut
|
||||
override lazy val errorStream: OutputStream = originalErr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.internal.util
|
||||
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import org.fusesource.jansi.internal.WindowsSupport
|
||||
import org.jline.utils.InfoCmp.Capability
|
||||
import scala.annotation.tailrec
|
||||
import Terminal.SimpleInputStream
|
||||
|
||||
/*
|
||||
* We need a special input stream for windows because special key events
|
||||
* like arrow keys are not reported by System.in. What makes this extra
|
||||
* tricky is that in canonical mode, ReadConsoleInput does not echo
|
||||
* characters, even when echo is set. The ReadConsole api does, but that
|
||||
* api isn't exposed by jansi so we can't use it without rolling our own
|
||||
* jni library or using the jna. That is more trouble than it's worth. To
|
||||
* work around the canonical mode issue, we can switch between reading using
|
||||
* ReadConsoleInput and just reading directly from the original System.in,
|
||||
* which does echo characters in canonical mode, when we enter and exit
|
||||
* raw mode.
|
||||
*/
|
||||
private[util] class WindowsInputStream(term: org.jline.terminal.Terminal, in: InputStream)
|
||||
extends SimpleInputStream {
|
||||
private val SHIFT_FLAG = 0x01;
|
||||
private val ALT_FLAG = 0x02;
|
||||
private val CTRL_FLAG = 0x04;
|
||||
|
||||
private val RIGHT_ALT_PRESSED = 0x0001;
|
||||
private val LEFT_ALT_PRESSED = 0x0002;
|
||||
private val RIGHT_CTRL_PRESSED = 0x0004;
|
||||
private val LEFT_CTRL_PRESSED = 0x0008;
|
||||
private val SHIFT_PRESSED = 0x0010;
|
||||
private def getCapability(cap: Capability): String = term.getStringCapability(cap) match {
|
||||
case null => null
|
||||
case c => c.replaceAllLiterally("\\E", "\u001B")
|
||||
}
|
||||
/*
|
||||
* This function is a hybrid of jline 2 WindowsTerminal.readConsoleInput
|
||||
* and jline3 AbstractTerminal.getEscapeSequence.
|
||||
*/
|
||||
private def readConsoleInput(): Array[Byte] = {
|
||||
WindowsSupport.readConsoleInput(1) match {
|
||||
case null => Array.empty
|
||||
case events =>
|
||||
val sb = new StringBuilder();
|
||||
events.foreach { event =>
|
||||
val keyEvent = event.keyEvent
|
||||
val controlKeyState = keyEvent.controlKeyState
|
||||
val isCtrl = (controlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) > 0;
|
||||
val isAlt = (controlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) > 0;
|
||||
val isShift = (controlKeyState & SHIFT_PRESSED) > 0;
|
||||
if (keyEvent.keyDown) {
|
||||
if (keyEvent.uchar > 0) {
|
||||
if (((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z'))
|
||||
&& isAlt && !isCtrl) {
|
||||
sb.append('\u001B') // ESC
|
||||
}
|
||||
if (isShift && keyEvent.keyCode == 9) {
|
||||
getCapability(Capability.key_btab) match {
|
||||
case null => sb.append(keyEvent.uchar)
|
||||
case cap => sb.append(cap)
|
||||
}
|
||||
} else {
|
||||
sb.append(keyEvent.uchar)
|
||||
}
|
||||
} else {
|
||||
// virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
// just add support for basic editing keys (no control state, no numpad keys)
|
||||
val escapeSequence = keyEvent.keyCode match {
|
||||
case 0x21 /* VK_PRIOR PageUp*/ => getCapability(Capability.key_ppage);
|
||||
case 0x22 /* VK_NEXT PageDown*/ => getCapability(Capability.key_npage);
|
||||
case 0x24 /* VK_HOME */ => getCapability(Capability.key_home)
|
||||
case 0x25 /* VK_LEFT */ => getCapability(Capability.key_left)
|
||||
case 0x26 /* VK_UP */ => getCapability(Capability.key_up)
|
||||
case 0x27 /* VK_RIGHT */ => getCapability(Capability.key_right)
|
||||
case 0x28 /* VK_DOWN */ => getCapability(Capability.key_down)
|
||||
case 0x70 /* VK_F1 */ => getCapability(Capability.key_f1)
|
||||
case 0x71 /* VK_F2 */ => getCapability(Capability.key_f2)
|
||||
case 0x72 /* VK_F3 */ => getCapability(Capability.key_f3)
|
||||
case 0x73 /* VK_F4 */ => getCapability(Capability.key_f4)
|
||||
case 0x74 /* VK_F5 */ => getCapability(Capability.key_f5)
|
||||
case 0x75 /* VK_F6 */ => getCapability(Capability.key_f6)
|
||||
case 0x76 /* VK_F7 */ => getCapability(Capability.key_f7)
|
||||
case 0x77 /* VK_F8 */ => getCapability(Capability.key_f8)
|
||||
case 0x78 /* VK_F9 */ => getCapability(Capability.key_f9)
|
||||
case 0x79 /* VK_F10 */ => getCapability(Capability.key_f10)
|
||||
case 0x7A /* VK_F11 */ => getCapability(Capability.key_f11)
|
||||
case 0x7B /* VK_F12 */ => getCapability(Capability.key_f12)
|
||||
// VK_END, VK_INSERT and VK_DELETE are not in the ansi key bindings so we
|
||||
// have to manually apply the the sequences here and in JLine3.wrap
|
||||
case 0x23 /* VK_END */ =>
|
||||
Option(getCapability(Capability.key_end)).getOrElse("\u001B[4!")
|
||||
case 0x2D /* VK_INSERT */ =>
|
||||
Option(getCapability(Capability.key_ic)).getOrElse("\u001B[2~")
|
||||
case 0x2E /* VK_DELETE */ =>
|
||||
Option(getCapability(Capability.key_dc)).getOrElse("\u001B[3~")
|
||||
case _ => null
|
||||
}
|
||||
escapeSequence match {
|
||||
case null =>
|
||||
case es => (0 until keyEvent.repeatCount.toInt).foreach(_ => sb.append(es))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// key up event
|
||||
// support ALT+NumPad input method
|
||||
if (keyEvent.keyCode == 0x12 /*VK_MENU ALT key*/ && keyEvent.uchar > 0) {
|
||||
sb.append(keyEvent.uchar);
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.toString().getBytes()
|
||||
}
|
||||
}
|
||||
private[this] val raw: InputStream = new SimpleInputStream {
|
||||
val buffer = new LinkedBlockingQueue[Integer]
|
||||
@tailrec
|
||||
override def read(): Int = {
|
||||
buffer.poll match {
|
||||
case null =>
|
||||
readConsoleInput().foreach(b => buffer.put(b & 0xFF))
|
||||
if (!Thread.interrupted) read() else throw new InterruptedException
|
||||
case b => b
|
||||
}
|
||||
}
|
||||
}
|
||||
private[this] val isRaw = new AtomicBoolean(true)
|
||||
private[sbt] def setRawMode(toggle: Boolean): Unit = isRaw.set(toggle)
|
||||
override def read(): Int = if (isRaw.get) raw.read() else in.read()
|
||||
}
|
||||
|
|
@ -85,6 +85,7 @@ public class BootServerSocket implements AutoCloseable {
|
|||
private final Object lock = new Object();
|
||||
private final LinkedBlockingQueue<ClientSocket> clientSocketReads = new LinkedBlockingQueue<>();
|
||||
private final Path socketFile;
|
||||
private final AtomicBoolean needInput = new AtomicBoolean(false);
|
||||
|
||||
private class ClientSocket implements AutoCloseable {
|
||||
final Socket socket;
|
||||
|
|
@ -116,13 +117,20 @@ public class BootServerSocket implements AutoCloseable {
|
|||
final InputStream inputStream = socket.getInputStream();
|
||||
while (alive.get()) {
|
||||
try {
|
||||
int b = inputStream.read();
|
||||
if (b != -1) {
|
||||
bytes.put(b);
|
||||
clientSocketReads.put(ClientSocket.this);
|
||||
} else {
|
||||
alive.set(false);
|
||||
synchronized (needInput) {
|
||||
while (!needInput.get() && alive.get()) needInput.wait();
|
||||
}
|
||||
if (alive.get()) {
|
||||
socket.getOutputStream().write(5);
|
||||
int b = inputStream.read();
|
||||
if (b != -1) {
|
||||
bytes.put(b);
|
||||
clientSocketReads.put(ClientSocket.this);
|
||||
} else {
|
||||
alive.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
alive.set(false);
|
||||
}
|
||||
|
|
@ -209,10 +217,18 @@ public class BootServerSocket implements AutoCloseable {
|
|||
@Override
|
||||
public int read() {
|
||||
try {
|
||||
synchronized (needInput) {
|
||||
needInput.set(true);
|
||||
needInput.notifyAll();
|
||||
}
|
||||
ClientSocket clientSocket = clientSocketReads.take();
|
||||
return clientSocket.bytes.take();
|
||||
} catch (final InterruptedException e) {
|
||||
return -1;
|
||||
} finally {
|
||||
synchronized (needInput) {
|
||||
needInput.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,8 +38,10 @@ import scala.util.{ Failure, Properties, Success, Try }
|
|||
import Serialization.{
|
||||
CancelAll,
|
||||
attach,
|
||||
cancelReadSystemIn,
|
||||
cancelRequest,
|
||||
promptChannel,
|
||||
readSystemIn,
|
||||
systemIn,
|
||||
systemErr,
|
||||
systemOut,
|
||||
|
|
@ -50,6 +52,8 @@ import Serialization.{
|
|||
terminalGetSize,
|
||||
terminalPropertiesQuery,
|
||||
terminalPropertiesResponse,
|
||||
terminalSetEcho,
|
||||
terminalSetRawMode,
|
||||
terminalSetSize,
|
||||
getTerminalAttributes,
|
||||
setTerminalAttributes,
|
||||
|
|
@ -147,13 +151,17 @@ class NetworkClient(
|
|||
}
|
||||
}
|
||||
|
||||
private[this] val stdinBytes = new LinkedBlockingQueue[Int]
|
||||
private[this] val stdinBytes = new LinkedBlockingQueue[Integer]
|
||||
private[this] val inLock = new Object
|
||||
private[this] val inputThread = new AtomicReference(new RawInputThread)
|
||||
private[this] val inputThread = new AtomicReference[RawInputThread]
|
||||
private[this] val exitClean = new AtomicBoolean(true)
|
||||
private[this] val sbtProcess = new AtomicReference[Process](null)
|
||||
private class ConnectionRefusedException(t: Throwable) extends Throwable(t)
|
||||
private class ServerFailedException extends Exception
|
||||
private[this] def startInputThread(): Unit = inputThread.get match {
|
||||
case null => inputThread.set(new RawInputThread)
|
||||
case _ =>
|
||||
}
|
||||
|
||||
// Open server connection based on the portfile
|
||||
def init(promptCompleteUsers: Boolean, retry: Boolean): ServerConnection =
|
||||
|
|
@ -165,8 +173,10 @@ class NetworkClient(
|
|||
if (noStdErr) System.exit(0)
|
||||
else if (noTab) waitForServer(portfile, log = true, startServer = true)
|
||||
else {
|
||||
stdinBytes.take match {
|
||||
case 9 =>
|
||||
startInputThread()
|
||||
stdinBytes.poll(5, TimeUnit.SECONDS) match {
|
||||
case null => System.exit(0)
|
||||
case i if i == 9 =>
|
||||
errorStream.println("\nStarting server...")
|
||||
waitForServer(portfile, !promptCompleteUsers, startServer = true)
|
||||
case _ => System.exit(0)
|
||||
|
|
@ -250,8 +260,13 @@ class NetworkClient(
|
|||
Option(inputThread.get).foreach(_.close())
|
||||
Option(interactiveThread.get).foreach(_.interrupt)
|
||||
}
|
||||
case "readInput" =>
|
||||
case _ => self.onNotification(msg)
|
||||
case `readSystemIn` => startInputThread()
|
||||
case `cancelReadSystemIn` =>
|
||||
inputThread.get match {
|
||||
case null =>
|
||||
case t => t.close()
|
||||
}
|
||||
case _ => self.onNotification(msg)
|
||||
}
|
||||
}
|
||||
override def onRequest(msg: JsonRpcRequestMessage): Unit = self.onRequest(msg)
|
||||
|
|
@ -289,9 +304,10 @@ class NetworkClient(
|
|||
var socket: Option[Socket] =
|
||||
if (!Properties.isLinux) Try(ClientSocket.localSocket(bootSocketName, useJNI)).toOption
|
||||
else None
|
||||
val term = Terminal.console
|
||||
term.exitRawMode()
|
||||
val process = socket match {
|
||||
case None if startServer =>
|
||||
val term = Terminal.console
|
||||
if (log) console.appendLog(Level.Info, "server was not detected. starting an instance")
|
||||
|
||||
val props =
|
||||
|
|
@ -349,6 +365,7 @@ class NetworkClient(
|
|||
s.getInputStream.read match {
|
||||
case -1 | 0 => readThreadAlive.set(false)
|
||||
case 2 => gotInputBack = true
|
||||
case 5 => term.enterRawMode(); startInputThread()
|
||||
case 3 if gotInputBack => readThreadAlive.set(false)
|
||||
case i if gotInputBack => stdinBytes.offer(i)
|
||||
case i => printStream.write(i)
|
||||
|
|
@ -381,9 +398,6 @@ class NetworkClient(
|
|||
while (!gotInputBack && !stdinBytes.isEmpty && socket.isDefined) {
|
||||
val out = s.getOutputStream
|
||||
val b = stdinBytes.poll
|
||||
// echo stdin during boot
|
||||
printStream.write(b)
|
||||
printStream.flush()
|
||||
out.write(b)
|
||||
out.flush()
|
||||
}
|
||||
|
|
@ -610,18 +624,13 @@ class NetworkClient(
|
|||
case (`terminalCapabilities`, Some(json)) =>
|
||||
Converter.fromJson[TerminalCapabilitiesQuery](json) match {
|
||||
case Success(terminalCapabilitiesQuery) =>
|
||||
val jline3 = terminalCapabilitiesQuery.jline3
|
||||
val response = TerminalCapabilitiesResponse(
|
||||
terminalCapabilitiesQuery.boolean
|
||||
.map(Terminal.console.getBooleanCapability(_, jline3)),
|
||||
.map(Terminal.console.getBooleanCapability(_)),
|
||||
terminalCapabilitiesQuery.numeric
|
||||
.map(
|
||||
c => Option(Terminal.console.getNumericCapability(c, jline3)).fold(-1)(_.toInt)
|
||||
),
|
||||
.map(c => Option(Terminal.console.getNumericCapability(c)).fold(-1)(_.toInt)),
|
||||
terminalCapabilitiesQuery.string
|
||||
.map(
|
||||
s => Option(Terminal.console.getStringCapability(s, jline3)).getOrElse("null")
|
||||
),
|
||||
.map(s => Option(Terminal.console.getStringCapability(s)).getOrElse("null")),
|
||||
)
|
||||
sendCommandResponse(
|
||||
terminalCapabilitiesResponse,
|
||||
|
|
@ -677,6 +686,21 @@ class NetworkClient(
|
|||
sendCommandResponse("", TerminalSetSizeResponse(), msg.id)
|
||||
case Failure(_) =>
|
||||
}
|
||||
case (`terminalSetEcho`, Some(json)) =>
|
||||
Converter.fromJson[TerminalSetEchoCommand](json) match {
|
||||
case Success(echo) =>
|
||||
Terminal.console.setEchoEnabled(echo.toggle)
|
||||
sendCommandResponse("", TerminalSetEchoResponse(), msg.id)
|
||||
case Failure(_) =>
|
||||
}
|
||||
case (`terminalSetRawMode`, Some(json)) =>
|
||||
Converter.fromJson[TerminalSetRawModeCommand](json) match {
|
||||
case Success(raw) =>
|
||||
if (raw.toggle) Terminal.console.enterRawMode()
|
||||
else Terminal.console.exitRawMode()
|
||||
sendCommandResponse("", TerminalSetRawModeResponse(), msg.id)
|
||||
case Failure(_) =>
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
|
@ -787,9 +811,11 @@ class NetworkClient(
|
|||
else if (noTab) updateCompletions()
|
||||
else {
|
||||
errorStream.print(s"\nNo cached $label names found. Press '<tab>' to compile: ")
|
||||
stdinBytes.take match {
|
||||
case 9 => updateCompletions()
|
||||
case _ => Nil
|
||||
startInputThread()
|
||||
stdinBytes.poll(5, TimeUnit.SECONDS) match {
|
||||
case null => Nil
|
||||
case i if i == 9 => updateCompletions()
|
||||
case _ => Nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -901,17 +927,18 @@ class NetworkClient(
|
|||
start()
|
||||
val stopped = new AtomicBoolean(false)
|
||||
override final def run(): Unit = {
|
||||
@tailrec def read(): Unit = {
|
||||
def read(): Unit = {
|
||||
inputStream.read match {
|
||||
case -1 =>
|
||||
case b =>
|
||||
inLock.synchronized(stdinBytes.offer(b))
|
||||
if (attached.get()) drain()
|
||||
if (!stopped.get()) read()
|
||||
}
|
||||
}
|
||||
try Terminal.console.withRawInput(read())
|
||||
catch { case _: InterruptedException | NonFatal(_) => stopped.set(true) }
|
||||
try read()
|
||||
catch { case _: InterruptedException | NonFatal(_) => stopped.set(true) } finally {
|
||||
inputThread.set(null)
|
||||
}
|
||||
}
|
||||
|
||||
def drain(): Unit = inLock.synchronized {
|
||||
|
|
@ -1098,6 +1125,7 @@ object NetworkClient {
|
|||
System.out.flush()
|
||||
})
|
||||
Runtime.getRuntime.addShutdownHook(hook)
|
||||
if (Util.isNonCygwinWindows) sbt.internal.util.JLine3.forceWindowsJansi()
|
||||
System.exit(Terminal.withStreams(false) {
|
||||
val term = Terminal.console
|
||||
try client(base, restOfArgs, term.inputStream, System.err, term, useJNI)
|
||||
|
|
|
|||
|
|
@ -64,12 +64,13 @@ public final class MetaBuildLoader extends URLClassLoader {
|
|||
* library.
|
||||
*/
|
||||
public static MetaBuildLoader makeLoader(final AppProvider appProvider) throws IOException {
|
||||
final Pattern pattern =
|
||||
Pattern.compile(
|
||||
"^(test-interface-[0-9.]+|jline-(terminal-)?[0-9.]+-sbt-.*|jansi-[0-9.]+)\\.jar");
|
||||
final String jlineJars = "jline-(terminal-)?[0-9.]+-sbt-.*|jline-terminal-(jna|jansi)-[0-9.]+";
|
||||
final String fullPattern =
|
||||
"^(test-interface-[0-9.]+|" + jlineJars + "|jansi-[0-9.]+|jna-(platform-)?[0-9.]+)\\.jar";
|
||||
final Pattern pattern = Pattern.compile(fullPattern);
|
||||
final File[] cp = appProvider.mainClasspath();
|
||||
final URL[] interfaceURLs = new URL[1];
|
||||
final URL[] jlineURLs = new URL[3];
|
||||
final URL[] jlineURLs = new URL[7];
|
||||
final File[] extra =
|
||||
appProvider.id().classpathExtra() == null ? new File[0] : appProvider.id().classpathExtra();
|
||||
final Set<File> bottomClasspath = new LinkedHashSet<>();
|
||||
|
|
|
|||
|
|
@ -386,6 +386,8 @@ object Defaults extends BuildCommon {
|
|||
sys.env.contains("CI") || SysProp.ci,
|
||||
// watch related settings
|
||||
pollInterval :== Watch.defaultPollInterval,
|
||||
canonicalInput :== true,
|
||||
echoInput :== true,
|
||||
) ++ LintUnused.lintSettings
|
||||
++ DefaultBackgroundJobService.backgroundJobServiceSettings
|
||||
++ RemoteCache.globalSettings
|
||||
|
|
@ -1700,6 +1702,20 @@ object Defaults extends BuildCommon {
|
|||
/** Implements `cleanFiles` task. */
|
||||
private[sbt] def cleanFilesTask: Initialize[Task[Vector[File]]] = Def.task { Vector.empty[File] }
|
||||
|
||||
private[this] def termWrapper(canonical: Boolean, echo: Boolean): (() => Unit) => (() => Unit) =
|
||||
(f: () => Unit) =>
|
||||
() => {
|
||||
val term = Terminal.get
|
||||
if (!canonical) {
|
||||
term.enterRawMode()
|
||||
if (echo) term.setEchoEnabled(echo)
|
||||
} else if (!echo) term.setEchoEnabled(false)
|
||||
try f()
|
||||
finally {
|
||||
if (!canonical) term.exitRawMode()
|
||||
if (!echo) term.setEchoEnabled(true)
|
||||
}
|
||||
}
|
||||
def bgRunMainTask(
|
||||
products: Initialize[Task[Classpath]],
|
||||
classpath: Initialize[Task[Classpath]],
|
||||
|
|
@ -1713,6 +1729,7 @@ object Defaults extends BuildCommon {
|
|||
val service = bgJobService.value
|
||||
val (mainClass, args) = parser.parsed
|
||||
val hashClasspath = (bgHashClasspath in bgRunMain).value
|
||||
val wrapper = termWrapper(canonicalInput.value, echoInput.value)
|
||||
service.runInBackgroundWithLoader(resolvedScoped.value, state.value) { (logger, workingDir) =>
|
||||
val files =
|
||||
if (copyClasspath.value)
|
||||
|
|
@ -1722,9 +1739,9 @@ object Defaults extends BuildCommon {
|
|||
scalaRun.value match {
|
||||
case r: Run =>
|
||||
val loader = r.newLoader(cp)
|
||||
(Some(loader), () => r.runWithLoader(loader, cp, mainClass, args, logger).get)
|
||||
(Some(loader), wrapper(() => r.runWithLoader(loader, cp, mainClass, args, logger).get))
|
||||
case sr =>
|
||||
(None, () => sr.run(mainClass, cp, args, logger).get)
|
||||
(None, wrapper(() => sr.run(mainClass, cp, args, logger).get))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1743,6 +1760,7 @@ object Defaults extends BuildCommon {
|
|||
val service = bgJobService.value
|
||||
val mainClass = mainClassTask.value getOrElse sys.error("No main class detected.")
|
||||
val hashClasspath = (bgHashClasspath in bgRun).value
|
||||
val wrapper = termWrapper(canonicalInput.value, echoInput.value)
|
||||
service.runInBackgroundWithLoader(resolvedScoped.value, state.value) { (logger, workingDir) =>
|
||||
val files =
|
||||
if (copyClasspath.value)
|
||||
|
|
@ -1753,9 +1771,9 @@ object Defaults extends BuildCommon {
|
|||
scalaRun.value match {
|
||||
case r: Run =>
|
||||
val loader = r.newLoader(cp)
|
||||
(Some(loader), () => r.runWithLoader(loader, cp, mainClass, args, logger).get)
|
||||
(Some(loader), wrapper(() => r.runWithLoader(loader, cp, mainClass, args, logger).get))
|
||||
case sr =>
|
||||
(None, () => sr.run(mainClass, cp, args, logger).get)
|
||||
(None, wrapper(() => sr.run(mainClass, cp, args, logger).get))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ object Keys {
|
|||
val logBuffered = settingKey[Boolean]("True if logging should be buffered until work completes.").withRank(CSetting)
|
||||
val sLog = settingKey[Logger]("Logger usable by settings during project loading.").withRank(CSetting)
|
||||
val serverLog = taskKey[Unit]("A dummy task to set server log level using Global / serverLog / logLevel.").withRank(CTask)
|
||||
val canonicalInput = settingKey[Boolean]("Toggles whether a task should use canonical input (line buffered with echo) or raw input").withRank(DSetting)
|
||||
val echoInput = settingKey[Boolean]("Toggles whether a task should echo user input").withRank(DSetting)
|
||||
|
||||
// Project keys
|
||||
val autoGeneratedProject = settingKey[Boolean]("If it exists, represents that the project (and name) were automatically created, rather than user specified.").withRank(DSetting)
|
||||
|
|
|
|||
|
|
@ -227,11 +227,11 @@ object StandardMain {
|
|||
// This is to workaround https://github.com/sbt/io/issues/110
|
||||
sys.props.put("jna.nosys", "true")
|
||||
|
||||
import BasicCommandStrings.isEarlyCommand
|
||||
import BasicCommandStrings.{ DashDashDetachStdio, DashDashServer, isEarlyCommand }
|
||||
val userCommands =
|
||||
configuration.arguments
|
||||
.map(_.trim)
|
||||
.filterNot(_ == BasicCommandStrings.DashDashDetachStdio)
|
||||
.filterNot(c => c == DashDashDetachStdio || c == DashDashServer)
|
||||
val (earlyCommands, normalCommands) = (preCommands ++ userCommands).partition(isEarlyCommand)
|
||||
val commands = (earlyCommands ++ normalCommands).toList map { x =>
|
||||
Exec(x, None)
|
||||
|
|
|
|||
|
|
@ -62,16 +62,14 @@ object ConsoleProject {
|
|||
val initCommands = importString + extra
|
||||
|
||||
val terminal = Terminal.get
|
||||
terminal.withCanonicalIn {
|
||||
// TODO - Hook up dsl classpath correctly...
|
||||
(new Console(compiler))(
|
||||
unit.classpath,
|
||||
options,
|
||||
initCommands,
|
||||
cleanupCommands,
|
||||
terminal
|
||||
)(Some(unit.loader), bindings).get
|
||||
}
|
||||
// TODO - Hook up dsl classpath correctly...
|
||||
(new Console(compiler))(
|
||||
unit.classpath,
|
||||
options,
|
||||
initCommands,
|
||||
cleanupCommands,
|
||||
terminal
|
||||
)(Some(unit.loader), bindings).get
|
||||
()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ import scala.collection.mutable
|
|||
import scala.concurrent.duration._
|
||||
import scala.util.Try
|
||||
import scala.util.control.NonFatal
|
||||
import Serialization.attach
|
||||
import Serialization.{ attach, cancelReadSystemIn, readSystemIn }
|
||||
|
||||
import sjsonnew._
|
||||
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter }
|
||||
|
|
@ -643,12 +643,19 @@ final class NetworkChannel(
|
|||
|
||||
private[this] lazy val inputStream: InputStream = new InputStream {
|
||||
override def read(): Int = {
|
||||
import sjsonnew.BasicJsonProtocol._
|
||||
try {
|
||||
jsonRpcNotify(readSystemIn, "")
|
||||
inputBuffer.take & 0xFF match {
|
||||
case -1 => throw new ClosedChannelException()
|
||||
case b => b
|
||||
}
|
||||
} catch { case e: IOException => -1 }
|
||||
} catch {
|
||||
case e: IOException =>
|
||||
try jsonRpcNotify(cancelReadSystemIn, "")
|
||||
catch { case _: IOException => }
|
||||
-1
|
||||
}
|
||||
}
|
||||
override def available(): Int = inputBuffer.size
|
||||
}
|
||||
|
|
@ -800,7 +807,7 @@ final class NetworkChannel(
|
|||
override def isAnsiSupported: Boolean = getProperty(_.isAnsiSupported, false).getOrElse(false)
|
||||
override def isEchoEnabled: Boolean = waitForPending(_.isEchoEnabled)
|
||||
override def isSuccessEnabled: Boolean =
|
||||
prompt != Prompt.Batch ||
|
||||
interactive.get ||
|
||||
StandardMain.exchange.withState(ContinuousCommands.isInWatch(_, NetworkChannel.this))
|
||||
override lazy val isColorEnabled: Boolean = waitForPending(_.isColorEnabled)
|
||||
override lazy val isSupershellEnabled: Boolean = waitForPending(_.isSupershellEnabled)
|
||||
|
|
@ -816,34 +823,19 @@ final class NetworkChannel(
|
|||
Some(result(queue.take))
|
||||
}
|
||||
}
|
||||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean =
|
||||
override def getBooleanCapability(capability: String): Boolean =
|
||||
getCapability(
|
||||
TerminalCapabilitiesQuery(
|
||||
boolean = Some(capability),
|
||||
numeric = None,
|
||||
string = None,
|
||||
jline3
|
||||
),
|
||||
TerminalCapabilitiesQuery(boolean = Some(capability), numeric = None, string = None),
|
||||
_.boolean.getOrElse(false)
|
||||
).getOrElse(false)
|
||||
override def getNumericCapability(capability: String, jline3: Boolean): Integer =
|
||||
override def getNumericCapability(capability: String): Integer =
|
||||
getCapability(
|
||||
TerminalCapabilitiesQuery(
|
||||
boolean = None,
|
||||
numeric = Some(capability),
|
||||
string = None,
|
||||
jline3
|
||||
),
|
||||
TerminalCapabilitiesQuery(boolean = None, numeric = Some(capability), string = None),
|
||||
(_: TerminalCapabilitiesResponse).numeric.map(Integer.valueOf).getOrElse(-1: Integer)
|
||||
).getOrElse(-1: Integer)
|
||||
override def getStringCapability(capability: String, jline3: Boolean): String =
|
||||
override def getStringCapability(capability: String): String =
|
||||
getCapability(
|
||||
TerminalCapabilitiesQuery(
|
||||
boolean = None,
|
||||
numeric = None,
|
||||
string = Some(capability),
|
||||
jline3
|
||||
),
|
||||
TerminalCapabilitiesQuery(boolean = None, numeric = None, string = Some(capability)),
|
||||
_.string.flatMap {
|
||||
case "null" => None
|
||||
case s => Some(s)
|
||||
|
|
@ -899,6 +891,25 @@ final class NetworkChannel(
|
|||
try queue.take
|
||||
catch { case _: InterruptedException => }
|
||||
}
|
||||
private[this] def setRawMode(toggle: Boolean): Unit = {
|
||||
if (!closed.get || false) {
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
val raw = TerminalSetRawModeCommand(toggle)
|
||||
val queue = VirtualTerminal.setTerminalRawMode(name, jsonRpcRequest, raw)
|
||||
try queue.take
|
||||
catch { case _: InterruptedException => }
|
||||
}
|
||||
}
|
||||
override private[sbt] def enterRawMode(): Unit = setRawMode(true)
|
||||
override private[sbt] def exitRawMode(): Unit = setRawMode(false)
|
||||
override def setEchoEnabled(toggle: Boolean): Unit =
|
||||
if (!closed.get) {
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
val echo = TerminalSetEchoCommand(toggle)
|
||||
val queue = VirtualTerminal.setTerminalEcho(name, jsonRpcRequest, echo)
|
||||
try queue.take
|
||||
catch { case _: InterruptedException => () }
|
||||
}
|
||||
|
||||
override def flush(): Unit = doFlush()
|
||||
override def toString: String = s"NetworkTerminal($name)"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ import sbt.protocol.Serialization.{
|
|||
terminalCapabilities,
|
||||
terminalGetSize,
|
||||
terminalPropertiesQuery,
|
||||
terminalSetEcho,
|
||||
terminalSetSize,
|
||||
terminalSetRawMode,
|
||||
}
|
||||
import sjsonnew.support.scalajson.unsafe.Converter
|
||||
import sbt.protocol.{
|
||||
|
|
@ -35,7 +37,9 @@ import sbt.protocol.{
|
|||
TerminalGetSizeQuery,
|
||||
TerminalGetSizeResponse,
|
||||
TerminalSetAttributesCommand,
|
||||
TerminalSetEchoCommand,
|
||||
TerminalSetSizeCommand,
|
||||
TerminalSetRawModeCommand,
|
||||
}
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
import sbt.protocol.TerminalGetSizeResponse
|
||||
|
|
@ -53,6 +57,10 @@ object VirtualTerminal {
|
|||
new ConcurrentHashMap[(String, String), ArrayBlockingQueue[Unit]]
|
||||
private[this] val pendingTerminalGetSize =
|
||||
new ConcurrentHashMap[(String, String), ArrayBlockingQueue[TerminalGetSizeResponse]]
|
||||
private[this] val pendingTerminalSetEcho =
|
||||
new ConcurrentHashMap[(String, String), ArrayBlockingQueue[Unit]]
|
||||
private[this] val pendingTerminalSetRawMode =
|
||||
new ConcurrentHashMap[(String, String), ArrayBlockingQueue[Unit]]
|
||||
private[sbt] def sendTerminalPropertiesQuery(
|
||||
channelName: String,
|
||||
jsonRpcRequest: (String, String, String) => Unit
|
||||
|
|
@ -134,6 +142,30 @@ object VirtualTerminal {
|
|||
queue
|
||||
}
|
||||
|
||||
private[sbt] def setTerminalEcho(
|
||||
channelName: String,
|
||||
jsonRpcRequest: (String, String, TerminalSetEchoCommand) => Unit,
|
||||
query: TerminalSetEchoCommand
|
||||
): ArrayBlockingQueue[Unit] = {
|
||||
val id = UUID.randomUUID.toString
|
||||
val queue = new ArrayBlockingQueue[Unit](1)
|
||||
pendingTerminalSetEcho.put((channelName, id), queue)
|
||||
jsonRpcRequest(id, terminalSetEcho, query)
|
||||
queue
|
||||
}
|
||||
|
||||
private[sbt] def setTerminalRawMode(
|
||||
channelName: String,
|
||||
jsonRpcRequest: (String, String, TerminalSetRawModeCommand) => Unit,
|
||||
query: TerminalSetRawModeCommand
|
||||
): ArrayBlockingQueue[Unit] = {
|
||||
val id = UUID.randomUUID.toString
|
||||
val queue = new ArrayBlockingQueue[Unit](1)
|
||||
pendingTerminalSetEcho.put((channelName, id), queue)
|
||||
jsonRpcRequest(id, terminalSetRawMode, query)
|
||||
queue
|
||||
}
|
||||
|
||||
val handler = ServerHandler { cb =>
|
||||
ServerIntent(requestHandler(cb), responseHandler(cb), notificationHandler(cb))
|
||||
}
|
||||
|
|
@ -193,6 +225,16 @@ object VirtualTerminal {
|
|||
case null =>
|
||||
case buffer => buffer.put(response.getOrElse(TerminalGetSizeResponse(1, 1)))
|
||||
}
|
||||
case r if pendingTerminalSetEcho.get((callback.name, r.id)) != null =>
|
||||
pendingTerminalSetEcho.remove((callback.name, r.id)) match {
|
||||
case null =>
|
||||
case buffer => buffer.put(())
|
||||
}
|
||||
case r if pendingTerminalSetRawMode.get((callback.name, r.id)) != null =>
|
||||
pendingTerminalSetRawMode.remove((callback.name, r.id)) match {
|
||||
case null =>
|
||||
case buffer => buffer.put(())
|
||||
}
|
||||
}
|
||||
private val notificationHandler: Handler[JsonRpcNotificationMessage] =
|
||||
callback => {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ object Dependencies {
|
|||
val jline3Version = "3.16.0" // Once the base jline version is upgraded, we can use the official jline-terminal
|
||||
val jline3Terminal = "org.scala-sbt.jline3" % "jline-terminal" % s"$jline3Version-sbt-211a082ed6326908dc84ca017ce4430728f18a8a"
|
||||
val jline3Jansi = "org.jline" % "jline-terminal-jansi" % jline3Version
|
||||
val jline3JNA = "org.jline" % "jline-terminal-jna" % jline3Version
|
||||
val jline3Reader = "org.jline" % "jline-reader" % jline3Version
|
||||
val jansi = "org.fusesource.jansi" % "jansi" % "1.18"
|
||||
val scalatest = "org.scalatest" %% "scalatest" % "3.0.8"
|
||||
|
|
|
|||
|
|
@ -7,23 +7,22 @@ package sbt.protocol
|
|||
final class TerminalCapabilitiesQuery private (
|
||||
val boolean: Option[String],
|
||||
val numeric: Option[String],
|
||||
val string: Option[String],
|
||||
val jline3: Boolean) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
val string: Option[String]) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: TerminalCapabilitiesQuery => (this.boolean == x.boolean) && (this.numeric == x.numeric) && (this.string == x.string) && (this.jline3 == x.jline3)
|
||||
case x: TerminalCapabilitiesQuery => (this.boolean == x.boolean) && (this.numeric == x.numeric) && (this.string == x.string)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (37 * (17 + "sbt.protocol.TerminalCapabilitiesQuery".##) + boolean.##) + numeric.##) + string.##) + jline3.##)
|
||||
37 * (37 * (37 * (37 * (17 + "sbt.protocol.TerminalCapabilitiesQuery".##) + boolean.##) + numeric.##) + string.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalCapabilitiesQuery(" + boolean + ", " + numeric + ", " + string + ", " + jline3 + ")"
|
||||
"TerminalCapabilitiesQuery(" + boolean + ", " + numeric + ", " + string + ")"
|
||||
}
|
||||
private[this] def copy(boolean: Option[String] = boolean, numeric: Option[String] = numeric, string: Option[String] = string, jline3: Boolean = jline3): TerminalCapabilitiesQuery = {
|
||||
new TerminalCapabilitiesQuery(boolean, numeric, string, jline3)
|
||||
private[this] def copy(boolean: Option[String] = boolean, numeric: Option[String] = numeric, string: Option[String] = string): TerminalCapabilitiesQuery = {
|
||||
new TerminalCapabilitiesQuery(boolean, numeric, string)
|
||||
}
|
||||
def withBoolean(boolean: Option[String]): TerminalCapabilitiesQuery = {
|
||||
copy(boolean = boolean)
|
||||
|
|
@ -43,12 +42,9 @@ final class TerminalCapabilitiesQuery private (
|
|||
def withString(string: String): TerminalCapabilitiesQuery = {
|
||||
copy(string = Option(string))
|
||||
}
|
||||
def withJline3(jline3: Boolean): TerminalCapabilitiesQuery = {
|
||||
copy(jline3 = jline3)
|
||||
}
|
||||
}
|
||||
object TerminalCapabilitiesQuery {
|
||||
|
||||
def apply(boolean: Option[String], numeric: Option[String], string: Option[String], jline3: Boolean): TerminalCapabilitiesQuery = new TerminalCapabilitiesQuery(boolean, numeric, string, jline3)
|
||||
def apply(boolean: String, numeric: String, string: String, jline3: Boolean): TerminalCapabilitiesQuery = new TerminalCapabilitiesQuery(Option(boolean), Option(numeric), Option(string), jline3)
|
||||
def apply(boolean: Option[String], numeric: Option[String], string: Option[String]): TerminalCapabilitiesQuery = new TerminalCapabilitiesQuery(boolean, numeric, string)
|
||||
def apply(boolean: String, numeric: String, string: String): TerminalCapabilitiesQuery = new TerminalCapabilitiesQuery(Option(boolean), Option(numeric), Option(string))
|
||||
}
|
||||
|
|
|
|||
32
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetEchoCommand.scala
generated
Normal file
32
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetEchoCommand.scala
generated
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class TerminalSetEchoCommand private (
|
||||
val toggle: Boolean) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: TerminalSetEchoCommand => (this.toggle == x.toggle)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (17 + "sbt.protocol.TerminalSetEchoCommand".##) + toggle.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalSetEchoCommand(" + toggle + ")"
|
||||
}
|
||||
private[this] def copy(toggle: Boolean = toggle): TerminalSetEchoCommand = {
|
||||
new TerminalSetEchoCommand(toggle)
|
||||
}
|
||||
def withToggle(toggle: Boolean): TerminalSetEchoCommand = {
|
||||
copy(toggle = toggle)
|
||||
}
|
||||
}
|
||||
object TerminalSetEchoCommand {
|
||||
|
||||
def apply(toggle: Boolean): TerminalSetEchoCommand = new TerminalSetEchoCommand(toggle)
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetEchoResponse.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetEchoResponse.scala
generated
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class TerminalSetEchoResponse private () extends sbt.protocol.EventMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case _: TerminalSetEchoResponse => true
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (17 + "sbt.protocol.TerminalSetEchoResponse".##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalSetEchoResponse()"
|
||||
}
|
||||
private[this] def copy(): TerminalSetEchoResponse = {
|
||||
new TerminalSetEchoResponse()
|
||||
}
|
||||
|
||||
}
|
||||
object TerminalSetEchoResponse {
|
||||
|
||||
def apply(): TerminalSetEchoResponse = new TerminalSetEchoResponse()
|
||||
}
|
||||
32
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetRawModeCommand.scala
generated
Normal file
32
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetRawModeCommand.scala
generated
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class TerminalSetRawModeCommand private (
|
||||
val toggle: Boolean) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case x: TerminalSetRawModeCommand => (this.toggle == x.toggle)
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (17 + "sbt.protocol.TerminalSetRawModeCommand".##) + toggle.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalSetRawModeCommand(" + toggle + ")"
|
||||
}
|
||||
private[this] def copy(toggle: Boolean = toggle): TerminalSetRawModeCommand = {
|
||||
new TerminalSetRawModeCommand(toggle)
|
||||
}
|
||||
def withToggle(toggle: Boolean): TerminalSetRawModeCommand = {
|
||||
copy(toggle = toggle)
|
||||
}
|
||||
}
|
||||
object TerminalSetRawModeCommand {
|
||||
|
||||
def apply(toggle: Boolean): TerminalSetRawModeCommand = new TerminalSetRawModeCommand(toggle)
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetRawModeReponse.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetRawModeReponse.scala
generated
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class TerminalSetRawModeReponse private () extends sbt.protocol.EventMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case _: TerminalSetRawModeReponse => true
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (17 + "sbt.protocol.TerminalSetRawModeReponse".##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalSetRawModeReponse()"
|
||||
}
|
||||
private[this] def copy(): TerminalSetRawModeReponse = {
|
||||
new TerminalSetRawModeReponse()
|
||||
}
|
||||
|
||||
}
|
||||
object TerminalSetRawModeReponse {
|
||||
|
||||
def apply(): TerminalSetRawModeReponse = new TerminalSetRawModeReponse()
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetRawModeResponse.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/protocol/TerminalSetRawModeResponse.scala
generated
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol
|
||||
final class TerminalSetRawModeResponse private () extends sbt.protocol.EventMessage() with Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = o match {
|
||||
case _: TerminalSetRawModeResponse => true
|
||||
case _ => false
|
||||
}
|
||||
override def hashCode: Int = {
|
||||
37 * (17 + "sbt.protocol.TerminalSetRawModeResponse".##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"TerminalSetRawModeResponse()"
|
||||
}
|
||||
private[this] def copy(): TerminalSetRawModeResponse = {
|
||||
new TerminalSetRawModeResponse()
|
||||
}
|
||||
|
||||
}
|
||||
object TerminalSetRawModeResponse {
|
||||
|
||||
def apply(): TerminalSetRawModeResponse = new TerminalSetRawModeResponse()
|
||||
}
|
||||
|
|
@ -6,6 +6,6 @@
|
|||
package sbt.protocol.codec
|
||||
|
||||
import _root_.sjsonnew.JsonFormat
|
||||
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.InitCommandFormats with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats with sbt.protocol.codec.AttachFormats with sbt.protocol.codec.TerminalCapabilitiesQueryFormats with sbt.protocol.codec.TerminalSetAttributesCommandFormats with sbt.protocol.codec.TerminalAttributesQueryFormats with sbt.protocol.codec.TerminalGetSizeQueryFormats with sbt.protocol.codec.TerminalSetSizeCommandFormats =>
|
||||
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat9[sbt.protocol.CommandMessage, sbt.protocol.InitCommand, sbt.protocol.ExecCommand, sbt.protocol.SettingQuery, sbt.protocol.Attach, sbt.protocol.TerminalCapabilitiesQuery, sbt.protocol.TerminalSetAttributesCommand, sbt.protocol.TerminalAttributesQuery, sbt.protocol.TerminalGetSizeQuery, sbt.protocol.TerminalSetSizeCommand]("type")
|
||||
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.InitCommandFormats with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats with sbt.protocol.codec.AttachFormats with sbt.protocol.codec.TerminalCapabilitiesQueryFormats with sbt.protocol.codec.TerminalSetAttributesCommandFormats with sbt.protocol.codec.TerminalAttributesQueryFormats with sbt.protocol.codec.TerminalGetSizeQueryFormats with sbt.protocol.codec.TerminalSetSizeCommandFormats with sbt.protocol.codec.TerminalSetEchoCommandFormats with sbt.protocol.codec.TerminalSetRawModeCommandFormats =>
|
||||
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat11[sbt.protocol.CommandMessage, sbt.protocol.InitCommand, sbt.protocol.ExecCommand, sbt.protocol.SettingQuery, sbt.protocol.Attach, sbt.protocol.TerminalCapabilitiesQuery, sbt.protocol.TerminalSetAttributesCommand, sbt.protocol.TerminalAttributesQuery, sbt.protocol.TerminalGetSizeQuery, sbt.protocol.TerminalSetSizeCommand, sbt.protocol.TerminalSetEchoCommand, sbt.protocol.TerminalSetRawModeCommand]("type")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@
|
|||
package sbt.protocol.codec
|
||||
|
||||
import _root_.sjsonnew.JsonFormat
|
||||
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.util.codec.JValueFormats with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats with sbt.protocol.codec.TerminalPropertiesResponseFormats with sbt.protocol.codec.TerminalCapabilitiesResponseFormats with sbt.protocol.codec.TerminalSetAttributesResponseFormats with sbt.protocol.codec.TerminalAttributesResponseFormats with sbt.protocol.codec.TerminalGetSizeResponseFormats with sbt.protocol.codec.TerminalSetSizeResponseFormats =>
|
||||
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat11[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure, sbt.protocol.TerminalPropertiesResponse, sbt.protocol.TerminalCapabilitiesResponse, sbt.protocol.TerminalSetAttributesResponse, sbt.protocol.TerminalAttributesResponse, sbt.protocol.TerminalGetSizeResponse, sbt.protocol.TerminalSetSizeResponse]("type")
|
||||
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.util.codec.JValueFormats with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats with sbt.protocol.codec.TerminalPropertiesResponseFormats with sbt.protocol.codec.TerminalCapabilitiesResponseFormats with sbt.protocol.codec.TerminalSetAttributesResponseFormats with sbt.protocol.codec.TerminalAttributesResponseFormats with sbt.protocol.codec.TerminalGetSizeResponseFormats with sbt.protocol.codec.TerminalSetSizeResponseFormats with sbt.protocol.codec.TerminalSetEchoResponseFormats with sbt.protocol.codec.TerminalSetRawModeResponseFormats =>
|
||||
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat13[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure, sbt.protocol.TerminalPropertiesResponse, sbt.protocol.TerminalCapabilitiesResponse, sbt.protocol.TerminalSetAttributesResponse, sbt.protocol.TerminalAttributesResponse, sbt.protocol.TerminalGetSizeResponse, sbt.protocol.TerminalSetSizeResponse, sbt.protocol.TerminalSetEchoResponse, sbt.protocol.TerminalSetRawModeResponse]("type")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
|||
with sbt.protocol.codec.TerminalAttributesQueryFormats
|
||||
with sbt.protocol.codec.TerminalGetSizeQueryFormats
|
||||
with sbt.protocol.codec.TerminalSetSizeCommandFormats
|
||||
with sbt.protocol.codec.TerminalSetEchoCommandFormats
|
||||
with sbt.protocol.codec.TerminalSetRawModeCommandFormats
|
||||
with sbt.protocol.codec.CommandMessageFormats
|
||||
with sbt.protocol.codec.CompletionParamsFormats
|
||||
with sbt.protocol.codec.ChannelAcceptedEventFormats
|
||||
|
|
@ -28,6 +30,8 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
|||
with sbt.protocol.codec.TerminalAttributesResponseFormats
|
||||
with sbt.protocol.codec.TerminalGetSizeResponseFormats
|
||||
with sbt.protocol.codec.TerminalSetSizeResponseFormats
|
||||
with sbt.protocol.codec.TerminalSetEchoResponseFormats
|
||||
with sbt.protocol.codec.TerminalSetRawModeResponseFormats
|
||||
with sbt.protocol.codec.EventMessageFormats
|
||||
with sbt.protocol.codec.SettingQueryResponseFormats
|
||||
with sbt.protocol.codec.CompletionResponseFormats
|
||||
|
|
|
|||
|
|
@ -14,9 +14,8 @@ implicit lazy val TerminalCapabilitiesQueryFormat: JsonFormat[sbt.protocol.Termi
|
|||
val boolean = unbuilder.readField[Option[String]]("boolean")
|
||||
val numeric = unbuilder.readField[Option[String]]("numeric")
|
||||
val string = unbuilder.readField[Option[String]]("string")
|
||||
val jline3 = unbuilder.readField[Boolean]("jline3")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalCapabilitiesQuery(boolean, numeric, string, jline3)
|
||||
sbt.protocol.TerminalCapabilitiesQuery(boolean, numeric, string)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
|
|
@ -26,7 +25,6 @@ implicit lazy val TerminalCapabilitiesQueryFormat: JsonFormat[sbt.protocol.Termi
|
|||
builder.addField("boolean", obj.boolean)
|
||||
builder.addField("numeric", obj.numeric)
|
||||
builder.addField("string", obj.string)
|
||||
builder.addField("jline3", obj.jline3)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetEchoCommandFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetEchoCommandFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalSetEchoCommandFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalSetEchoCommandFormat: JsonFormat[sbt.protocol.TerminalSetEchoCommand] = new JsonFormat[sbt.protocol.TerminalSetEchoCommand] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetEchoCommand = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val toggle = unbuilder.readField[Boolean]("toggle")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalSetEchoCommand(toggle)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalSetEchoCommand, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("toggle", obj.toggle)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetEchoResponseFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetEchoResponseFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalSetEchoResponseFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalSetEchoResponseFormat: JsonFormat[sbt.protocol.TerminalSetEchoResponse] = new JsonFormat[sbt.protocol.TerminalSetEchoResponse] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetEchoResponse = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalSetEchoResponse()
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalSetEchoResponse, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetRawModeCommandFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetRawModeCommandFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalSetRawModeCommandFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalSetRawModeCommandFormat: JsonFormat[sbt.protocol.TerminalSetRawModeCommand] = new JsonFormat[sbt.protocol.TerminalSetRawModeCommand] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetRawModeCommand = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val toggle = unbuilder.readField[Boolean]("toggle")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalSetRawModeCommand(toggle)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalSetRawModeCommand, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("toggle", obj.toggle)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetRawModeReponseFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetRawModeReponseFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalSetRawModeReponseFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalSetRawModeReponseFormat: JsonFormat[sbt.protocol.TerminalSetRawModeReponse] = new JsonFormat[sbt.protocol.TerminalSetRawModeReponse] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetRawModeReponse = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalSetRawModeReponse()
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalSetRawModeReponse, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetRawModeResponseFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/protocol/codec/TerminalSetRawModeResponseFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait TerminalSetRawModeResponseFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val TerminalSetRawModeResponseFormat: JsonFormat[sbt.protocol.TerminalSetRawModeResponse] = new JsonFormat[sbt.protocol.TerminalSetRawModeResponse] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.TerminalSetRawModeResponse = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.TerminalSetRawModeResponse()
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.protocol.TerminalSetRawModeResponse, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -97,7 +97,6 @@ type TerminalCapabilitiesQuery implements CommandMessage {
|
|||
boolean: String
|
||||
numeric: String
|
||||
string: String
|
||||
jline3: Boolean!
|
||||
}
|
||||
|
||||
type TerminalCapabilitiesResponse implements EventMessage {
|
||||
|
|
@ -138,3 +137,15 @@ type TerminalSetSizeCommand implements CommandMessage {
|
|||
}
|
||||
|
||||
type TerminalSetSizeResponse implements EventMessage {}
|
||||
|
||||
type TerminalSetEchoCommand implements CommandMessage {
|
||||
toggle: Boolean!
|
||||
}
|
||||
|
||||
type TerminalSetEchoResponse implements EventMessage {}
|
||||
|
||||
type TerminalSetRawModeCommand implements CommandMessage {
|
||||
toggle: Boolean!
|
||||
}
|
||||
|
||||
type TerminalSetRawModeResponse implements EventMessage {}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import sbt.internal.protocol.{
|
|||
|
||||
object Serialization {
|
||||
private[sbt] val VsCode = "application/vscode-jsonrpc; charset=utf-8"
|
||||
val readSystemIn = "sbt/readSystemIn"
|
||||
val cancelReadSystemIn = "sbt/cancelReadSystemIn"
|
||||
val systemIn = "sbt/systemIn"
|
||||
val systemOut = "sbt/systemOut"
|
||||
val systemErr = "sbt/systemErr"
|
||||
|
|
@ -41,6 +43,8 @@ object Serialization {
|
|||
val getTerminalAttributes = "sbt/getTerminalAttributes"
|
||||
val terminalGetSize = "sbt/terminalGetSize"
|
||||
val terminalSetSize = "sbt/terminalSetSize"
|
||||
val terminalSetEcho = "sbt/terminalSetEcho"
|
||||
val terminalSetRawMode = "sbt/terminalSetRawMode"
|
||||
val CancelAll = "__CancelAll"
|
||||
|
||||
@deprecated("unused", since = "1.4.0")
|
||||
|
|
|
|||
Loading…
Reference in New Issue